Application instances

Although somewhat hidden in a modern task switcher, it can be noticed that some operating systems prefer to have just one instance of an application open rather than launching many (for example, one per document). When developing applications for macOS, it's encouraged to only have one copy running at any time-attempting to run a second instance of the same application will normally result in the original window being brought to the front. If your application is aiming to support platforms where the concurrent instances semantics are different, time should be spent deciding how your application should behave. Will your design work the same way on all platforms or adapt to the current environment?

To change behavior according to platform, it's possible to detect the operating system at runtime, but normally the operating system is the defining factor, and so you can make use of Go's built in build tags discussed in Chapter 3Go to the Rescue!. For example, we could have two different files that control how an application is opened: launch_darwin.go will be used when compiling for macOS and launch_other.go will run on other platforms. An example of setting this up may go as follows.

Firstly, we create a file that handles the standard mechanism (called launch_other.go);  opening a file or a new document will create a window with an appropriate document and show it:

// +build !darwin
package main

type app struct {
}

func (a *app) openFile(file string) {
newWindow(openDocument(file)).Show()
}

func (a *app) openBlank() {
newWindow(newDocument()).Show()
}

Then, we make a version for macOS (named launch_darwin.go), which first checks for a running instance. If one is found, we call some RPC (remote proceedure call) functions to open files in the running application, otherwise we load the window as before:

package main

import (
"log"
"os"
)

type app struct {
}

func (a *app) openFile(file string) {
running := getFirstInstance(a)
if running != nil {
log.Println("Found running app, opening document", file)
running.openFile(file)
os.Exit(0)
} else {
newWindow(openDocument(file)).Show()
}
}

func (a *app) openBlank() {
running := getFirstInstance(a)
if running != nil {
log.Println("Found running app, opening blank document")
running.openBlank()
os.Exit(0)
} else {
newWindow(newDocument()).Show()
}
}

The main function of the app that launches this will probably be to parse the command-line parameters to determine whether a filename has been passed, such as the following:

func main() {
app := &app{}

if len(os.Args) <= 1 {
app.openBlank()
} else {
app.openFile(os.Args[1])
}
}

The details of getFirstInstance() and the RPC code are out of scope for this chapter but can be found in the chapter11/singleapp folder in this book's code repository. This model may be supported by some toolkits, but there are also projects that aim to make this easier, such as https://github.com/marcsauter/single.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.139.86.18