Part 3: Enhancing Your Wails App with Offline Functionality and Native Integrations

Welcome to Part 3 of our Wails series! So far, we’ve built a lightweight desktop app using Go and modern web frameworks, integrated external APIs, and packaged the app for cross-platform distribution. Now, we’re going to enhance your Wails app with offline functionality and native integrations to make it more resilient and seamless for users.

In this post, we will cover:

  • Implementing offline caching for your Wails app.

  • Integrating platform-specific features.

Let’s dive in!

Step 1: Implement Offline Caching

Offline functionality ensures your app remains usable even without an internet connection. We’ll use a combination of Go (backend) and localStorage in the frontend.

Backend Logic: Caching Data

First, let’s store data fetched from the API in a local file using Go. Update main.go:

func (a *App) FetchJokeWithCache() (string, error) {
	cacheFile := "C:\\temp\\cache.json"

	// Check if cache exists
	if _, err := os.Stat(cacheFile); err == nil {
		file, err := os.Open(cacheFile)
		if err != nil {
			panic(err)
		}
		defer file.Close()

		data, _ := io.ReadAll(file)
		return string(data), nil
	}

	// Fetch from API
	resp, err := http.Get("https://official-joke-api.appspot.com/random_joke")
	if err != nil {
		return "", err
	}
	defer resp.Body.Close()

	body, _ := io.ReadAll(resp.Body)

	var joke Joke
	if err := json.Unmarshal(body, &joke); err != nil {
		return "", err
	}

	marshal, err := json.Marshal(joke)
	if err != nil {
		return "", err
	}

	file, err := os.OpenFile(cacheFile, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
	if err != nil {
		panic(err)
	}

	if _, err := file.Write(marshal); err != nil {
		file.Close() // ignore error; Write error takes precedence
	}
	if err := file.Close(); err != nil {
		panic(err)
	}

	return string(marshal), nil
}

Here’s what we did:

  1. Check if a cache.json file exists.

  2. If it exists, return the cached data.

  3. Otherwise, fetch new data from the API, cache it, and return it.

Frontend Logic: Display Cached Data

Update your React code to call the new backend function:

Now your app will display cached jokes when offline!

Step 2: Integrate Platform-Specific Features

Wails supports adding platform-specific functionality. For example, on Windows, you might want to minimize the app to the system tray, while on macOS, you could use menu bar integration.

Platform Check

You can detect the OS in your Go code:

func (a *App) GetPlatform() string {
	return runtime.GOOS
}

Call this function in your React frontend to conditionally display features:

function App() {
    const [joke, setJoke] = useState({
        setup: "Click the button to hear a new joke.",
        punchline: ""
    });
    const updateJoke = (result: string) => setJoke(JSON.parse(result));

    function fetchJoke() {
        FetchJoke().then(updateJoke);
    }

    const [jokeCache, setJokeCache] = useState({
        setup: "Click the button to hear a cached joke.",
        punchline: ""
    });
    const updateJokeCache = (result: string) => setJokeCache(JSON.parse(result));

    function fetchJokeWithCache() {
        FetchJokeWithCache().then(updateJokeCache);
    }

    const checkPlatform = async () => {
        const os = await GetPlatform();
        alert(`You are running on ${os}`);
    };

    return (
        <div id="App">
            <img src={logo} id="logo" alt="logo"/>
            <div id="input" className="input-box">
                <button className="btn" onClick={fetchJoke}>Joke</button>
            </div>
            <div id="setup" className="setup">{joke.setup}</div>
            <div id="punchline" className="punchline">{joke.punchline}</div>
            <hr/>
            <div id="input" className="input-box">
                <button className="btn" onClick={fetchJokeWithCache}>Cache</button>
            </div>
            <div id="setup" className="setup">{jokeCache.setup}</div>
            <div id="punchline" className="punchline">{jokeCache.punchline}</div>
            <hr/>
            <div id="input" className="input-box">
                <button className="btn" onClick={checkPlatform}>Platform</button>
            </div>
        </div>
    )
}

Step 3: Testing Offline Functionality

To test your offline caching, simply disconnect from the internet and restart the app. The cached data will load seamlessly!

Conclusion

With offline caching and platform-specific integrations, your Wails app is now more resilient, user-friendly, and tailored for a native desktop experience. These enhancements allow your app to stand out with performance and native-like behavior.

Stay tuned for Part 4, where we’ll explore deploying your Wails app and automating updates for users. 🚀

Next
Next

Building Your First Wails App: Beyond Basics