Skip to content

Instantly share code, notes, and snippets.

@nathankerr
Last active July 25, 2025 12:08
Show Gist options
  • Save nathankerr/38d8b0d45590741b57f5f79be336f07c to your computer and use it in GitHub Desktop.
Save nathankerr/38d8b0d45590741b57f5f79be336f07c to your computer and use it in GitHub Desktop.

Revisions

  1. nathankerr created this gist Feb 20, 2017.
    31 changes: 31 additions & 0 deletions Info.plist
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,31 @@
    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
    <plist version="1.0">
    <dict>
    <key>CFBundleExecutable</key>
    <string>myapp</string>
    <key>CFBundleIdentifier</key>
    <string>com.pocketgophers.myapp</string>
    <key>CFBundleURLTypes</key>
    <array>
    <dict>
    <key>CFBundleURLName</key>
    <string>com.pocketgophers.myapp</string>
    <key>CFBundleURLSchemes</key>
    <array>
    <string>myapp</string>
    </array>
    </dict>
    </array>
    <key>CFBundleInfoDictionaryVersion</key>
    <string>6.0</string>
    <key>CFBundleName</key>
    <string>MyApp</string>
    <key>CFBundlePackageType</key>
    <string>APPL</string>
    <key>CFBundleShortVersionString</key>
    <string>1.0.0</string>
    <key>CFBundleVersion</key>
    <string>20</string>
    </dict>
    </plist>
    17 changes: 17 additions & 0 deletions Makefile
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,17 @@
    myapp.app: *.go *.h *.m Makefile Info.plist
    mkdir -p myapp.app/Contents/MacOS
    go build -i -o myapp.app/Contents/MacOS/myapp
    cp Info.plist myapp.app/Contents/Info.plist

    .PHONY: open
    open: myapp.app
    open myapp.app

    .PHONY: scheme
    scheme: myapp.app
    open myapp://hello/world

    .PHONY: clean
    clean:
    rm -rf myapp.app

    9 changes: 9 additions & 0 deletions handler.h
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,9 @@
    #import <Foundation/Foundation.h>

    extern void HandleURL(char*);

    @interface GoPasser : NSObject
    + (void)handleGetURLEvent:(NSAppleEventDescriptor *)event;
    @end

    void StartURLHandler(void);
    15 changes: 15 additions & 0 deletions handler.m
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,15 @@
    #include "handler.h"

    @implementation GoPasser
    + (void)handleGetURLEvent:(NSAppleEventDescriptor *)event
    {
    HandleURL([[[event paramDescriptorForKeyword:keyDirectObject] stringValue] UTF8String]);
    }
    @end

    void StartURLHandler(void) {
    NSAppleEventManager *appleEventManager = [NSAppleEventManager sharedAppleEventManager];
    [appleEventManager setEventHandler:[GoPasser class]
    andSelector:@selector(handleGetURLEvent:)
    forEventClass:kInternetEventClass andEventID:kAEGetURL];
    }
    56 changes: 56 additions & 0 deletions main.go
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,56 @@
    package main

    /*
    #cgo CFLAGS: -x objective-c
    #cgo LDFLAGS: -framework Foundation
    #include "handler.h"
    */
    import "C"

    import (
    "log"

    "github.com/andlabs/ui"
    )

    // Sources used to figure this out:
    // Info.plist: https://gist.github.com/chrissnell/db95a3c5ad6ceca4c673e96cca0f7548
    // custom url handler example: http://fredandrandall.com/blog/2011/07/30/how-to-launch-your-macios-app-with-a-custom-url/
    // obj-c example: https://gist.github.com/leepro/016d7d6b61021dfc67daf61771c92b3c
    // note: import .h, not .m

    var labelText chan string

    func main() {
    log.SetFlags(log.Lshortfile)

    labelText = make(chan string, 1) // the event handler blocks!, so buffer the channel at least once to get the first message
    C.StartURLHandler()

    err := ui.Main(func() {
    greeting := ui.NewLabel("greeting\nline 2")
    window := ui.NewWindow("Hello", 200, 100, true)
    window.SetChild(greeting)
    window.OnClosing(func(*ui.Window) bool {
    ui.Quit()
    return true
    })
    window.Show()

    go func() {
    for text := range labelText {
    ui.QueueMain(func() {
    greeting.SetText(text)
    })
    }
    }()
    })
    if err != nil {
    log.Fatal(err)
    }
    }

    //export HandleURL
    func HandleURL(u *C.char) {
    labelText <- C.GoString(u)
    }