How to Create Your First Mac App Using Go

Nile BitsNile Bits
6 min read

Introduction

Mac App development has traditionally relied on programming languages like Swift and Objective-C. However, Go's efficiency and flexibility make it an excellent choice for creating robust yet simple Mac applications. In this tutorial, we'll guide you step-by-step through the process of building, testing, and deploying your first Mac app using Go, starting with setting up your development environment.

Why Use Go for Mac App Development?

Go, also known as Golang, is a statically typed, compiled language designed by Google. It has gained popularity due to its simplicity, performance, and efficient concurrency handling. Here's why you might consider using Go for Mac app development:

  1. Simplicity: Go's syntax is straightforward and easy to learn, making it a great choice for developers of all levels.

  2. Performance: Being a compiled language, Go is fast and efficient, which is crucial for creating responsive desktop applications.

  3. Cross-Platform Capabilities: While this guide focuses on macOS, Go's cross-platform nature means you can easily adapt your app for other operating systems.

  4. Concurrency: Go has built-in support for concurrent programming, allowing you to create apps that can handle multiple tasks simultaneously without slowing down.

Prerequisites

Before diving into the code, ensure you have the following tools installed:

  • Go: Install the latest version from the official Go website.

  • Xcode Command Line Tools: Install these by running xcode-select --install in the terminal.

  • Gio: Gio is a library for writing portable graphical user interfaces in Go. It simplifies the process of building GUIs and is perfect for Mac app development. You can install Gio using go get -u gioui.org/cmd/gogio.

Step 1: Setting Up Your Go Environment

First, you need to configure your Go environment properly:

  1. Install Go: Download and install Go from the official site. Follow the installation instructions for your operating system.

  2. Set Up Your Workspace: Go uses a workspace to organize your projects. By default, the workspace is located in ~/go, but you can change this by setting the GOPATH environment variable.

   mkdir -p ~/go/src/github.com/yourusername
   export GOPATH=~/go
  1. Install Gio: Gio is a toolkit for building native applications for Android, Linux, and macOS. Install Gio by running:
   go get -u gioui.org/cmd/gogio

Step 2: Creating a Basic Mac App

Let's create a simple "Hello World" Mac app using Gio.

  1. Initialize Your Project: Create a new directory for your project and navigate to it.
   mkdir HelloWorldMacApp
   cd HelloWorldMacApp
  1. Create the Main Go File: Create a file named main.go and open it in your favorite text editor.
   touch main.go
  1. Write the Code: Start by writing a basic Go program that initializes a window and displays "Hello World".
   package main

   import (
       "gioui.org/app"
       "gioui.org/io/system"
       "gioui.org/layout"
       "gioui.org/op"
       "gioui.org/widget/material"
       "gioui.org/font/gofont"
   )

   func main() {
       go func() {
           // Create a new window.
           w := app.NewWindow()
           th := material.NewTheme(gofont.Collection())

           for e := range w.Events() {
               switch e := e.(type) {
               case system.FrameEvent:
                   gtx := layout.NewContext(&op.Ops{}, e)
                   layout.Center.Layout(gtx, func(gtx layout.Context) layout.Dimensions {
                       return material.H1(th, "Hello, World!").Layout(gtx)
                   })
                   e.Frame(gtx.Ops)
               case system.DestroyEvent:
                   return
               }
           }
       }()
       app.Main()
   }
  1. Build and Run Your App: To build and run your app, use the following command:
   go run main.go

This should open a new window displaying "Hello, World!".

Step 3: Enhancing Your App with a Button

Now that we have a basic app running, let's enhance it by adding a button that displays a message when clicked.

  1. Modify main.go: Update your main.go file to include a button.
   package main

   import (
       "gioui.org/app"
       "gioui.org/io/system"
       "gioui.org/layout"
       "gioui.org/op"
       "gioui.org/widget"
       "gioui.org/widget/material"
       "gioui.org/font/gofont"
   )

   func main() {
       go func() {
           // Create a new window.
           w := app.NewWindow()
           th := material.NewTheme(gofont.Collection())

           var button widget.Clickable

           for e := range w.Events() {
               switch e := e.(type) {
               case system.FrameEvent:
                   gtx := layout.NewContext(&op.Ops{}, e)
                   layout.Center.Layout(gtx, func(gtx layout.Context) layout.Dimensions {
                       return layout.Flex{Axis: layout.Vertical}.Layout(gtx,
                           layout.Rigid(material.H1(th, "Hello, World!").Layout),
                           layout.Rigid(material.Button(th, &button, "Click Me").Layout),
                       )
                   })

                   if button.Clicked() {
                       println("Button clicked!")
                   }

                   e.Frame(gtx.Ops)
               case system.DestroyEvent:
                   return
               }
           }
       }()
       app.Main()
   }
  1. Build and Run Your Enhanced App: Run the app again with go run main.go. This time, you should see a "Click Me" button below the "Hello, World!" text. Clicking the button will print "Button clicked!" to the console.

Step 4: Adding More Features

Let's add more features to our app, such as text input and a dropdown menu.

  1. Adding Text Input: Modify your main.go to include a text input field.
   package main

   import (
       "gioui.org/app"
       "gioui.org/io/system"
       "gioui.org/layout"
       "gioui.org/op"
       "gioui.org/widget"
       "gioui.org/widget/material"
       "gioui.org/font/gofont"
   )

   func main() {
       go func() {
           // Create a new window.
           w := app.NewWindow()
           th := material.NewTheme(gofont.Collection())

           var button widget.Clickable
           var textField widget.Editor

           for e := range w.Events() {
               switch e := e.(type) {
               case system.FrameEvent:
                   gtx := layout.NewContext(&op.Ops{}, e)
                   layout.Center.Layout(gtx, func(gtx layout.Context) layout.Dimensions {
                       return layout.Flex{Axis: layout.Vertical}.Layout(gtx,
                           layout.Rigid(material.H1(th, "Hello, World!").Layout),
                           layout.Rigid(material.Editor(th, &textField, "Enter text...").Layout),
                           layout.Rigid(material.Button(th, &button, "Click Me").Layout),
                       )
                   })

                   if button.Clicked() {
                       println("Button clicked with text:", textField.Text())
                   }

                   e.Frame(gtx.Ops)
               case system.DestroyEvent:
                   return
               }
           }
       }()
       app.Main()
   }
  1. Adding a Dropdown Menu: Add a dropdown menu to your app.
   package main

   import (
       "gioui.org/app"
       "gioui.org/io/system"
       "gioui.org/layout"
       "gioui.org/op"
       "gioui.org/widget"
       "gioui.org/widget/material"
       "gioui.org/font/gofont"
   )

   func main() {
       go func() {
           // Create a new window.
           w := app.NewWindow()
           th := material.NewTheme(gofont.Collection())

           var button widget.Clickable
           var textField widget.Editor
           var list widget.List

           list.Axis = layout.Vertical

           items := []string{"Item 1", "Item 2", "Item 3"}

           for e := range w.Events() {
               switch e := e.(type) {
               case system.FrameEvent:
                   gtx := layout.NewContext(&op.Ops{}, e)
                   layout.Center.Layout(gtx, func(gtx layout.Context) layout.Dimensions {
                       return layout.Flex{Axis: layout.Vertical}.Layout(gtx,
                           layout.Rigid(material.H1(th, "Hello, World!").Layout),
                           layout.Rigid(material.Editor(th, &textField, "Enter text...").Layout),
                           layout.Rigid(material.Button(th, &button, "Click Me").Layout),
                           layout.Rigid(material.List(th, &list).Layout(gtx, len(items), func(gtx layout.Context, index int) layout.Dimensions {
                               return material.Button(th, new(widget.Clickable), items[index]).Layout(gtx)
                           })),
                       )
                   })

                   if button.Clicked() {
                       println("Button clicked with text:", textField.Text())
                   }

                   e.Frame(gtx.Ops)


 case system.DestroyEvent:
                   return
               }
           }
       }()
       app.Main()
   }
  1. Run Your App: Run your app again with go run main.go and see the new features in action.

Step 5: Building a Standalone Mac App

Once your app is ready, you'll want to build it as a standalone application. Follow these steps:

  1. Build Your App: Use gogio to build your app for macOS.
   gogio -target darwin .

This command will generate a .app bundle that you can run directly on macOS.

  1. Test Your App: Open the generated .app bundle to test your application. Ensure all features work as expected.

Step 6: Packaging and Distribution

To distribute your app, you may want to sign and notarize it for macOS.

  1. Sign Your App: Code signing is required to distribute your app outside of the Mac App Store. Use the codesign tool to sign your app.
   codesign --deep --force --verify --verbose --sign "Developer ID Application: Your Name" HelloWorldMacApp.app
  1. Notarize Your App: To ensure macOS allows your app to run without warning, notarize it using xcrun altool.
   xcrun altool --notarize-app --primary-bundle-id "com.yourname.helloworldmacapp" --username "yourappleid@example.com" --password "app-specific-password" --file HelloWorldMacApp.zip
  1. Distribute Your App: Once notarized, you can distribute your app via your website, email, or other means.

Conclusion

Congratulations! You've successfully created your first Mac app using Go. This guide covered the basics of setting up your development environment, building a simple app, adding features, and distributing your application. With Go's simplicity and performance, you're well-equipped to develop powerful, efficient Mac apps. Continue exploring Gio and Go to enhance your skills and create more complex applications.

References

This blog post provides a comprehensive guide to building your first Mac app using Go, with plenty of code examples to help you understand each step. By following this guide, you can quickly get started with Mac app development and explore the powerful capabilities of Go and Gio.

0
Subscribe to my newsletter

Read articles from Nile Bits directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Nile Bits
Nile Bits

Nile Bits is a software company, focusing on outsourcing software development and custom software solutions. Our outsourcing software services and solutions are designed with a focus on secure, scalable, expandable and reliable business systems. Via our low cost, high quality and reliable outsourcing software services, we provide to our clients value for money and therefore client satisfaction.