Announcement

👇Official Account👇

Welcome to join the group & private message

Article first/tail QR code

Skip to content

Mac Desktop Application Development with Wails

In the Go language ecosystem, Wails is an excellent cross-platform desktop application development framework. This article will introduce in detail how to use Wails to develop Mac desktop applications, including project creation, architecture design, frontend-backend communication and other core content.

Recently, I've been learning Wails development and created a Mac desktop application based on Wails and Web technology stack. During this process, I gained a deep understanding of Wails' architectural design and development workflow. Now I'll record the entire development process, hoping to provide reference for other developers.

🎯 Wails Framework Introduction

What is Wails?

Wails is a project that allows you to write desktop applications using Go and Web technologies. Think of it as Go's fast and lightweight alternative to Electron. Wails comes with many pre-configured templates that let you quickly get up and running with applications. There are templates for the following frameworks: Svelte, React, Vue, Preact, Lit and Vanilla. Each template has JavaScript and TypeScript versions.

Wails' Core Advantages

  1. Lightweight: Compared to Electron, Wails is more lightweight and doesn't need to package the entire Chromium
  2. High Performance: Uses system-native WebView with excellent performance
  3. Cross-platform: Supports Windows, macOS, Linux three major platforms
  4. Development Efficiency: Supports hot reload with good development experience
  5. Flexible Technology Stack: Supports multiple frontend frameworks and languages

Technical Architecture

Wails adopts a frontend-backend separation architecture design:

  • Backend: Go language provides business logic and system API access
  • Frontend: Web technology stack (HTML/CSS/JavaScript) provides user interface
  • Communication: Data interaction between frontend and backend through APIs provided by Wails

Wails Official Documentation

📱 Project Effect Showcase

Application Interface

This is a system monitoring tool with main features including:

  • CPU Usage Monitoring: Real-time display of CPU usage
  • Memory Usage Monitoring: Display memory usage status
  • System Information Display: Display CPU core count and other system information
  • Real-time Data Updates: Real-time data refresh through WebSocket

Technical Features

  • Lightweight Design: Small application size, fast startup
  • Real-time Monitoring: Supports real-time monitoring of system resources
  • Beautiful Interface: Adopts modern UI design
  • Cross-platform Support: Cross-platform compatibility based on Wails

🚀 Project Creation and Initialization

Environment Preparation

Before starting development, ensure that Go language environment and Wails CLI tools are installed:

bash
# Install Wails CLI
go install github.com/wailsapp/wails/v2/cmd/wails@latest

# Verify installation
wails doctor

Create Project

Use Wails CLI to create a new project:

bash
wails init -n wails_demo -t https://github.com/KiddoV/wails-pure-js-template

Command Parameter Description:

  • wails init: Initialize new project
  • -n wails_demo: Specify project name as wails_demo
  • -t https://github.com/KiddoV/wails-pure-js-template: Specify using pure HTML/JS template

Template Selection

Wails provides multiple frontend template choices:

Template TypeUse CasesComplexity
VueModern SPA applicationsMedium
ReactComplex interactive applicationsMedium
SvelteLightweight applicationsSimple
Vanilla JSSimple applicationsSimple
LitWeb component applicationsMedium

This project chooses pure HTML/JS template, suitable for rapid prototyping and simple applications.

📁 Project Structure Analysis

Directory Structure

bash
├── README.md                    # Project documentation
├── app.go                       # Application logic file
├── build/                       # Build output directory
   ├── README.md
   ├── appicon.png              # Application icon
   ├── bin/                     # Executable files
   └── pf_tools.app         # macOS application package
   ├── darwin/                  # macOS build configuration
   └── windows/                 # Windows build configuration
├── frontend/                    # Frontend code directory
   ├── src/                     # Source code
   ├── assets/              # Static resources
   ├── fonts/           # Font files
   └── images/          # Image resources
   ├── libs/                # Third-party libraries
   ├── echarts/         # Chart library
   ├── jquery-3.4.1/    # jQuery library
   ├── layui/           # UI framework
   └── live2d/          # Live2D animation
   ├── index.html           # Main page
   ├── main.css             # Style file
   └── main.js              # Main logic file
   └── wailsjs/                 # Wails generated JS bindings
├── go.mod                       # Go module file
├── go.sum                       # Dependency verification file
├── main.go                      # Application entry file
├── pkg/                         # Package directory
   └── sys/                     # System-related packages
       └── sys.go               # System monitoring logic
├── test/                        # Test files
└── wails.json                   # Wails configuration file

Core File Description

FilePurposeImportanceDescription
main.goApplication entry, window configuration⭐⭐⭐⭐⭐Application startup and window configuration
app.goApplication logic, frontend-backend communication⭐⭐⭐⭐⭐Business logic and API binding
pkg/sys/sys.goSystem monitoring functionality implementation⭐⭐⭐⭐System resource monitoring core
frontend/src/index.htmlFrontend main page⭐⭐⭐⭐User interface entry
frontend/src/main.jsFrontend logic processing⭐⭐⭐⭐Frontend interaction logic
wails.jsonWails project configuration⭐⭐⭐Project metadata configuration

Architecture Design Philosophy

The project adopts a clear layered architecture design:

  1. Application Layer: main.go responsible for application startup and window configuration
  2. Business Layer: app.go handles business logic and frontend-backend communication
  3. Service Layer: pkg/sys/sys.go provides system monitoring services
  4. Presentation Layer: frontend/ directory contains all frontend code

This layered design makes the code structure clear and easy to maintain and extend.

🎨 Frontend Layout and Configuration

Window Configuration Design

The initial design goal was to create a system monitoring tool suitable for 8.8-inch secondary screens, so the window configuration adopted a fixed-size design:

go
// main.go - Window configuration
err := wails.Run(&options.App{
    Title:             "PF_tools",           // Application title
    Width:             1280,                 // Window width (suitable for secondary screen)
    Height:            320,                  // Window height (compact design)
    MinWidth:          1280,                 // Minimum width (fixed size)
    MinHeight:         320,                  // Minimum height (fixed size)
    DisableResize:     true,                 // Disable window resizing
    Fullscreen:        false,                // Non-fullscreen mode
    Frameless:         false,                // Keep window borders
    StartHidden:       false,                // Show on startup
    HideWindowOnClose: true,                 // Hide instead of exit when closing
    BackgroundColour:  &options.RGBA{R: 16, G: 12, B: 42, A: 255}, // Dark background
    AlwaysOnTop:       true,                 // Window always on top
    Menu:              nil,                  // No menu bar
    Logger:            nil,                  // Default log configuration
    LogLevel:          logger.DEBUG,         // Debug log level
    OnStartup:         app.startup,          // Startup callback function
    OnDomReady:        app.domReady,         // DOM ready callback
    OnBeforeClose:     app.beforeClose,      // Before close callback
    OnShutdown:        app.shutdown,         // Shutdown callback
    WindowStartState:  options.Normal,       // Normal window state
    Bind: []interface{}{
        app,                                 // Bind application instance to frontend
    },
})

Design Considerations:

  • Fixed Size: 1280x320 suitable for secondary screen display, avoiding user misoperation changing window size
  • Window Always on Top: Ensure monitoring information is always visible
  • Dark Theme: Reduce visual fatigue, suitable for long-term display
  • Hide Instead of Exit: Keep application running in background for quick recovery

HTML Page Structure

html
<!DOCTYPE html>
<html>
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
    <meta http-equiv="X-UA-Compatible" content="IE=Edge, Chrome=1">
    <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
    <link rel="icon" type="image/png" href="data:image/png;base64,iVBORw0KGgo=">
    
    <!-- Style file imports -->
    <link rel="stylesheet" href="../libs/layui/css/layui.css"/>
    <link rel="stylesheet" href="../libs/live2d/assets/waifu.css"/>
    <link rel="stylesheet" href="main.css"/>
    
    <!-- JavaScript library imports -->
    <script src="../libs/jquery-3.4.1/jquery-3.4.1.min.js"></script>
    <script src="../libs/echarts/echarts.min.js"></script>
    <script src="../libs/layui/layui.js"></script>
    <script src="../libs/live2d/assets/waifu-tips.js"></script>
    <script src="../libs/live2d/assets/live2d.js"></script>
    <script src="main.js"></script>
</head>

<body id="app" class="app" style="--wails-draggable:drag">

Key Configuration Description:

  1. Resource Paths: Use relative paths ../libs/ to reference third-party libraries, ensuring correct loading in Wails environment
  2. Window Dragging: style="--wails-draggable:drag" enables window dragging functionality, improving user experience
  3. Compatibility: Set X-UA-Compatible to ensure consistent display across different browsers
  4. Responsive: viewport configuration supports responsive layout

JavaScript Interaction Logic

javascript
function event_cpu_on() {
    // Use Layui framework
    layui.use(function () {
        // Listen for CPU usage events
        runtime.EventsOn("cpu_usage", function (cpu_usage) {
            // Update CPU usage display
            document.getElementById("used").textContent = cpu_usage.avg + '% '
        })
    })

    // Call Go backend method to get CPU information
    window.go.main.App.CpuInfo().then(result => {
        // Parse returned JSON data
        res = JSON.parse(result)
        // Update CPU core count display
        document.getElementById("cpu_num").textContent = res.cpu_number
    }).catch(err => {
        console.log(err);
    }).finally(() => {
        console.log("finished!")
    });
}

Communication Mechanism Details:

  1. Event Listening: runtime.EventsOn("cpu_usage", callback) listens for CPU usage events sent by Go backend

    • Backend sends data through runtime.EventsEmit(ctx, "cpu_usage", data)
    • Frontend receives in real-time and updates UI display
  2. Method Calls: window.go.main.App.CpuInfo() directly calls Go backend methods

    • Handle asynchronous calls through Promise
    • Return JSON format data, frontend parses and updates interface
  3. Error Handling: Use .catch() and .finally() to ensure program stability

🔧 Technical Implementation Details

Frontend-backend Communication Mechanism

Wails provides multiple frontend-backend communication methods. This project mainly uses the following two:

1. Event-driven Communication

go
// app.go - Backend event sending
func (a *App) startup(ctx context.Context) {
    // Start timer, send CPU usage every second
    go func() {
        ticker := time.NewTicker(time.Second)
        defer ticker.Stop()
        
        for {
            select {
            case <-ticker.C:
                cpuUsage := getCPUUsage()
                runtime.EventsEmit(ctx, "cpu_usage", cpuUsage)
            case <-ctx.Done():
                return
            }
        }
    }()
}
javascript
// main.js - Frontend event listening
function event_cpu_on() {
    layui.use(function () {
        runtime.EventsOn("cpu_usage", function (cpu_usage) {
            document.getElementById("used").textContent = cpu_usage.avg + '% '
        })
    })
}

2. Method Call Communication

go
// app.go - Backend method definition
func (a *App) CpuInfo() string {
    info := getCPUInfo()
    jsonData, _ := json.Marshal(info)
    return string(jsonData)
}
javascript
// main.js - Frontend method call
window.go.main.App.CpuInfo().then(result => {
    res = JSON.parse(result)
    document.getElementById("cpu_num").textContent = res.cpu_number
}).catch(err => {
    console.log(err);
});

System Monitoring Implementation

CPU Usage Monitoring

go
// pkg/sys/sys.go
func getCPUUsage() map[string]interface{} {
    var cpuUsage map[string]interface{}
    
    // Get CPU statistics
    cpuStats, err := cpu.Percent(time.Second, false)
    if err != nil {
        return map[string]interface{}{"avg": 0}
    }
    
    if len(cpuStats) > 0 {
        cpuUsage = map[string]interface{}{
            "avg": cpuStats[0],
            "timestamp": time.Now().Unix(),
        }
    }
    
    return cpuUsage
}

Memory Usage Monitoring

go
// pkg/sys/sys.go
func getMemoryInfo() map[string]interface{} {
    vmstat, err := mem.VirtualMemory()
    if err != nil {
        return map[string]interface{}{}
    }
    
    return map[string]interface{}{
        "total": vmstat.Total,
        "used": vmstat.Used,
        "free": vmstat.Free,
        "percent": vmstat.UsedPercent,
    }
}

🚀 Development and Building

Development Mode

bash
# Start development mode (supports hot reload)
wails dev

# Start with specified port
wails dev -port 8080

# Start in debug mode
wails dev -debug

Building Applications

bash
# Build for all platforms
wails build

# Build for specific platforms
wails build -platform darwin/amd64
wails build -platform darwin/arm64
wails build -platform windows/amd64
wails build -platform linux/amd64

# Build and package
wails build -package

Performance Optimization

  1. Resource Optimization: Use CDN or local caching for third-party libraries
  2. Code Splitting: Load JavaScript modules on demand
  3. Image Optimization: Use WebP format and appropriate compression
  4. Memory Management: Clean up event listeners and timers in time

📊 Project Summary

Technical Highlights

  1. Real-time Monitoring: Real-time system resource monitoring through WebSocket
  2. Cross-platform Compatibility: Windows/macOS/Linux support based on Wails
  3. Lightweight Design: Smaller application size and faster startup compared to Electron
  4. Modern UI: Adopts responsive design, supports multiple screen sizes

Development Experience

  1. Frontend-backend Separation: Clear architecture design facilitates maintenance and extension
  2. Event-driven: Reasonable use of event mechanisms for real-time data updates
  3. Error Handling: Complete error handling mechanisms improve application stability
  4. Performance Monitoring: Continuous monitoring of application performance, timely optimization

Extension Suggestions

  1. Add More Monitoring Indicators: Disk usage, network status, etc.
  2. Implement Data Persistence: Save historical monitoring data
  3. Add Alert Functions: Send alerts when resource usage exceeds thresholds
  4. Optimize UI Interactions: Add more interactive features and animation effects

📚 Project Resources

Complete Code

Project complete code hosted on GitHub: https://github.com/pfinal-nc/wails_pf

Quick Start

bash
# Clone project
git clone git@github.com:pfinal-nc/wails_pf.git

# Enter project directory
cd wails_pf

# Build application
wails build

Build Output

After building, you can find the packaged application files in the build/bin/ directory:

  • macOS: pf_tools.app (macOS application package)
  • Windows: wails_tools.exe (Windows executable file)
  • Linux: wails_tools (Linux executable file)

Running Applications

bash
# macOS
open build/bin/pf_tools.app

# Windows
./build/bin/wails_tools.exe

# Linux
./build/bin/wails_tools

Tags: #Go #Wails #Desktop Applications #System Monitoring #Cross-platform Development #macOS

Last updated: