search
HomeBackend DevelopmentGolangBuilding a City Hall Clock App for macOS: A Comprehensive Guide

Building a City Hall Clock App for macOS: A Comprehensive Guide

Ready to build a cool City Hall Clock app for your Mac? Great! We're going to create an app that sits in your menu bar, chimes every 15 minutes, and even counts out the hours. Let's break it down step by step, and I'll explain every part of the code so you can understand what's going on.

Project Overview

Our City Hall Clock app will:

  • Display a clock icon in the macOS menu bar
  • Chime every 15 minutes
  • Chime the number of hours at the top of each hour
  • Provide a "Quit" option in the menu bar
  • Run as a proper macOS application without opening a terminal window

Setting Up the Project

First things first, let's set up our project:

  1. Create a new directory:
   mkdir CityHallClock
   cd CityHallClock
  1. Initialize a new Go module:
   go mod init cityhallclock
  1. Install the required dependencies:
   go get github.com/getlantern/systray
   go get github.com/faiface/beep

The Main Code

Now, let's create our main.go file and go through each function:

package main

import (
    "bytes"
    "log"
    "os"
    "path/filepath"
    "time"

    "github.com/faiface/beep"
    "github.com/faiface/beep/mp3"
    "github.com/faiface/beep/speaker"
    "github.com/getlantern/systray"
)

var (
    audioBuffer *beep.Buffer
)

func main() {
    initAudio()
    systray.Run(onReady, onExit)
}

// ... (other functions will go here)

Let's break down each function:

1. main() Function

func main() {
    initAudio()
    systray.Run(onReady, onExit)
}

This is where our app starts. It does two important things:

  1. Calls initAudio() to set up our chime sound.
  2. Runs our systray app, telling it what to do when it's ready (onReady) and when it's quitting (onExit).

2. initAudio() Function

func initAudio() {
    execPath, err := os.Executable()
    if err != nil {
        log.Fatal(err)
    }
    resourcesPath := filepath.Join(filepath.Dir(execPath), "..", "Resources")
    chimeFile := filepath.Join(resourcesPath, "chime.mp3")

    f, err := os.Open(chimeFile)
    if err != nil {
        log.Fatal(err)
    }
    defer f.Close()

    streamer, format, err := mp3.Decode(f)
    if err != nil {
        log.Fatal(err)
    }
    defer streamer.Close()

    audioBuffer = beep.NewBuffer(format)
    audioBuffer.Append(streamer)

    err = speaker.Init(format.SampleRate, format.SampleRate.N(time.Second/10))
    if err != nil {
        log.Fatal(err)
    }
}

This function sets up our audio:

  1. It finds where our app is running and locates the chime sound file.
  2. Opens the MP3 file and decodes it.
  3. Creates an audio buffer with the chime sound.
  4. Initializes the audio speaker.

If anything goes wrong (like not finding the sound file), it'll log the error and quit.

3. onReady() Function

func onReady() {
    systray.SetIcon(getIcon())
    systray.SetTitle("City Hall Clock")
    systray.SetTooltip("City Hall Clock")

    mQuit := systray.AddMenuItem("Quit", "Quit the app")

    go func() {
        



<p>This function sets up our menu bar icon:</p>

<ol>
<li>Sets the icon (using getIcon()).</li>
<li>Sets the title and tooltip.</li>
<li>Adds a "Quit" option to the menu.</li>
<li>Starts listening for when the "Quit" option is clicked.</li>
<li>Starts running our clock (in a separate goroutine so it doesn't block).</li>
</ol>

<h3>
  
  
  4. onExit() Function
</h3>



<pre class="brush:php;toolbar:false">func onExit() {
    // Cleanup tasks go here
}

This function is called when the app is quitting. We're not doing anything here, but you could add cleanup tasks if needed.

5. runClock() Function

func runClock() {
    ticker := time.NewTicker(time.Minute)
    defer ticker.Stop()

    for {
        select {
        case t := 



<p>This is our clock's "heart":</p>

<ol>
<li>It creates a ticker that "ticks" every minute.</li>
<li>In an infinite loop, it checks the time every minute.</li>
<li>If it's the top of the hour or quarter past, it triggers the chime.</li>
</ol>

<h3>
  
  
  6. chime() Function
</h3>



<pre class="brush:php;toolbar:false">func chime(t time.Time) {
    hour := t.Hour()
    minute := t.Minute()

    var chimeTimes int
    if minute == 0 {
        chimeTimes = hour % 12
        if chimeTimes == 0 {
            chimeTimes = 12
        }
    } else {
        chimeTimes = 1
    }

    for i := 0; i 



<p>This function plays our chimes:</p>

<ol>
<li>It figures out how many times to chime (once for quarter-hours, or the number of the hour at the top of the hour).</li>
<li>It then plays the chime sound that many times, with a short pause between chimes.</li>
</ol>

<h3>
  
  
  7. getIcon() Function
</h3>



<pre class="brush:php;toolbar:false">func getIcon() []byte {
    execPath, err := os.Executable()
    if err != nil {
        log.Fatal(err)
    }
    iconPath := filepath.Join(filepath.Dir(execPath), "..", "Resources", "icon.png")

    // Read the icon file
    icon, err := os.ReadFile(iconPath)
    if err != nil {
        log.Fatal(err)
    }

    return icon
}

This function gets our menu bar icon:

  1. It finds where our app is running.
  2. Locates the icon file in the Resources directory.
  3. Reads the icon file and returns its contents.

Creating the macOS Application Bundle

To make our app a proper macOS citizen, we need to create an application bundle. This involves creating an Info.plist file:

<?xml version="1.0" encoding="UTF-8"?>

<plist version="1.0">
<dict>
    <key>CFBundleExecutable</key>
    <string>CityHallClock</string>
    <key>CFBundleIconFile</key>
    <string>AppIcon</string>
    <key>CFBundleIdentifier</key>
    <string>com.yourcompany.cityhallclock</string>
    <key>CFBundleName</key>
    <string>City Hall Clock</string>
    <key>CFBundlePackageType</key>
    <string>APPL</string>
    <key>CFBundleShortVersionString</key>
    <string>1.0</string>
    <key>CFBundleVersion</key>
    <string>1</string>
    <key>LSMinimumSystemVersion</key>
    <string>10.12</string>
    <key>LSUIElement</key>
    <true></true>
    <key>NSHighResolutionCapable</key>
    <true></true>
</dict>
</plist>

Save this as Info.plist in your project directory.

Adding Custom Icons

We need two icons:

  1. Menu Bar Icon: Create a 22x22 pixel PNG named icon.png.
  2. App Icon: Create an .icns file:
    • Make images sized 16x16 to 1024x1024 pixels.
    • Save them in AppIcon.iconset with names like icon_16x16.png.
    • Run: iconutil -c icns AppIcon.iconset

Building and Packaging

Let's create a build script (build.sh):

#!/bin/bash

# Build the Go application
go build -o CityHallClock

# Create the app bundle structure
mkdir -p CityHallClock.app/Contents/MacOS
mkdir -p CityHallClock.app/Contents/Resources

# Move the executable to the app bundle
mv CityHallClock CityHallClock.app/Contents/MacOS/

# Copy the Info.plist
cp Info.plist CityHallClock.app/Contents/

# Copy the chime sound to Resources
cp chime.mp3 CityHallClock.app/Contents/Resources/

# Copy the menu bar icon
cp icon.png CityHallClock.app/Contents/Resources/

# Copy the application icon
cp AppIcon.icns CityHallClock.app/Contents/Resources/

echo "Application bundle created: CityHallClock.app"

Make it executable with chmod +x build.sh, then run it with ./build.sh.

Conclusion

And there you have it! You've built a fully functional City Hall Clock app for macOS. You've learned about:

  • Creating a menu bar app with Go
  • Playing sounds at specific intervals
  • Packaging a Go application as a native macOS app

Feel free to expand on this. Maybe add preferences for custom chimes or different chiming intervals. The sky's the limit!

You can find full source code here https://github.com/rezmoss/citychime

Happy coding, and enjoy your new clock!

The above is the detailed content of Building a City Hall Clock App for macOS: A Comprehensive Guide. For more information, please follow other related articles on the PHP Chinese website!

Statement
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn
Learn Go String Manipulation: Working with the 'strings' PackageLearn Go String Manipulation: Working with the 'strings' PackageMay 09, 2025 am 12:07 AM

Go's "strings" package provides rich features to make string operation efficient and simple. 1) Use strings.Contains() to check substrings. 2) strings.Split() can be used to parse data, but it should be used with caution to avoid performance problems. 3) strings.Join() is suitable for formatting strings, but for small datasets, looping = is more efficient. 4) For large strings, it is more efficient to build strings using strings.Builder.

Go: String Manipulation with the Standard 'strings' PackageGo: String Manipulation with the Standard 'strings' PackageMay 09, 2025 am 12:07 AM

Go uses the "strings" package for string operations. 1) Use strings.Join function to splice strings. 2) Use the strings.Contains function to find substrings. 3) Use the strings.Replace function to replace strings. These functions are efficient and easy to use and are suitable for various string processing tasks.

Mastering Byte Slice Manipulation with Go's 'bytes' Package: A Practical GuideMastering Byte Slice Manipulation with Go's 'bytes' Package: A Practical GuideMay 09, 2025 am 12:02 AM

ThebytespackageinGoisessentialforefficientbyteslicemanipulation,offeringfunctionslikeContains,Index,andReplaceforsearchingandmodifyingbinarydata.Itenhancesperformanceandcodereadability,makingitavitaltoolforhandlingbinarydata,networkprotocols,andfileI

Learn Go Binary Encoding/Decoding: Working with the 'encoding/binary' PackageLearn Go Binary Encoding/Decoding: Working with the 'encoding/binary' PackageMay 08, 2025 am 12:13 AM

Go uses the "encoding/binary" package for binary encoding and decoding. 1) This package provides binary.Write and binary.Read functions for writing and reading data. 2) Pay attention to choosing the correct endian (such as BigEndian or LittleEndian). 3) Data alignment and error handling are also key to ensure the correctness and performance of the data.

Go: Byte Slice Manipulation with the Standard 'bytes' PackageGo: Byte Slice Manipulation with the Standard 'bytes' PackageMay 08, 2025 am 12:09 AM

The"bytes"packageinGooffersefficientfunctionsformanipulatingbyteslices.1)Usebytes.Joinforconcatenatingslices,2)bytes.Bufferforincrementalwriting,3)bytes.Indexorbytes.IndexByteforsearching,4)bytes.Readerforreadinginchunks,and5)bytes.SplitNor

Go encoding/binary package: Optimizing performance for binary operationsGo encoding/binary package: Optimizing performance for binary operationsMay 08, 2025 am 12:06 AM

Theencoding/binarypackageinGoiseffectiveforoptimizingbinaryoperationsduetoitssupportforendiannessandefficientdatahandling.Toenhanceperformance:1)Usebinary.NativeEndianfornativeendiannesstoavoidbyteswapping.2)BatchReadandWriteoperationstoreduceI/Oover

Go bytes package: short reference and tipsGo bytes package: short reference and tipsMay 08, 2025 am 12:05 AM

Go's bytes package is mainly used to efficiently process byte slices. 1) Using bytes.Buffer can efficiently perform string splicing to avoid unnecessary memory allocation. 2) The bytes.Equal function is used to quickly compare byte slices. 3) The bytes.Index, bytes.Split and bytes.ReplaceAll functions can be used to search and manipulate byte slices, but performance issues need to be paid attention to.

Go bytes package: practical examples for byte slice manipulationGo bytes package: practical examples for byte slice manipulationMay 08, 2025 am 12:01 AM

The byte package provides a variety of functions to efficiently process byte slices. 1) Use bytes.Contains to check the byte sequence. 2) Use bytes.Split to split byte slices. 3) Replace the byte sequence bytes.Replace. 4) Use bytes.Join to connect multiple byte slices. 5) Use bytes.Buffer to build data. 6) Combined bytes.Map for error processing and data verification.

See all articles

Hot AI Tools

Undresser.AI Undress

Undresser.AI Undress

AI-powered app for creating realistic nude photos

AI Clothes Remover

AI Clothes Remover

Online AI tool for removing clothes from photos.

Undress AI Tool

Undress AI Tool

Undress images for free

Clothoff.io

Clothoff.io

AI clothes remover

Video Face Swap

Video Face Swap

Swap faces in any video effortlessly with our completely free AI face swap tool!

Hot Tools

DVWA

DVWA

Damn Vulnerable Web App (DVWA) is a PHP/MySQL web application that is very vulnerable. Its main goals are to be an aid for security professionals to test their skills and tools in a legal environment, to help web developers better understand the process of securing web applications, and to help teachers/students teach/learn in a classroom environment Web application security. The goal of DVWA is to practice some of the most common web vulnerabilities through a simple and straightforward interface, with varying degrees of difficulty. Please note that this software

Atom editor mac version download

Atom editor mac version download

The most popular open source editor

VSCode Windows 64-bit Download

VSCode Windows 64-bit Download

A free and powerful IDE editor launched by Microsoft

SublimeText3 Mac version

SublimeText3 Mac version

God-level code editing software (SublimeText3)

ZendStudio 13.5.1 Mac

ZendStudio 13.5.1 Mac

Powerful PHP integrated development environment