Skip to content
programmingkidx edited this page Feb 5, 2024 · 1 revision

How to use nib files with DarwinKit

Assumptions made in this tutorial

  • Have Xcode installed

  • Have DarwinKit installed

  • Using Go 1.18 or higher

  • Running Mac OS

System Requirements

  • Xcode version: unknown

  • Mac OS version: unknown

  • Go version: 1.18 or higher

     +
    Do you like to program in Go? +
    Do you want to make programs for Mac OS (or another Apple operating
    system)? +
    Do you want your applications to be disk space and RAM efficient? +
    Do you want to use the AppKit framework to make your application's
    GUI? +
    Do you want to create an user interface with drag and drop ease?

If you said yes to these questions then you are in the right place.

With DarwinKit you can create full Mac OS applications. There is no need to use code to create the user interface. You can use nib files and Xcode to create them. This tutorial will take you step by step thru the application creation process.

Directions:
<img width="733" alt="NewXIB" src="https://github.com/programmingkidx/macdriver/assets/19289541/4459e977-b1ca-4bba-8d67-0b3de7c56ee0">

  1. Start Xcode.

  2. Create a new empty XIB file by going to File → New → macOS→ User Interface → Empty.
    new empty XIB file dialog

     +
    . Push the Next button and save the file as MainMenu.xib.
    . Show the library by going to View->Show Library. +
    image:formatted%20pictures/ShowLibrary.png[Show Library menu
    item,width=433,height=389] +
     +
    . In the search field at the top enter "window".
    . A window appears in the search results.
    . Drag this window object to the middle of the screen while holding down
    the Option key. +
    Note: Holding down the Option key initially makes the Library window
    stay open for subsequent drags.
    . Next search for a push button.
    . Drag this control to the window.
    . Then find a textfield and drag it onto the window. +
    image:formatted%20pictures/AddingTextfield.png[Adding the textfield to
    the window,width=833,height=406] +
     +
    . You may close the Library window now if you wish.
    . Click on the textfield to select it. +
    . Click on this button to show the Attributes Inspector. +
    image:formatted%20pictures/AttributeInspectorButton.png[Attribute
    Inspector button,width=326,height=343] +
     +
    . In the Attributes window scroll down to the View section.
    . In the Tag field enter 1. +
    image:formatted%20pictures/Tag.png[Setting the
    tag,width=361,height=175] +
     +
    . Do the same thing for the push button except enter 2 this time.
    . Click on the window's title bar. +
    image:formatted%20pictures/WindowTitlebar.png[Window's
    ttilebar,width=432,height=216] +
    . In the Attributes window under the Window selection enter "My Go
    Application" in the Title section. This will set the window's title to
    "My Go Application". +
    image:formatted%20pictures/SetWindowTitle.png[Set the window
    title,width=316,height=276] +
    . Click on the button.
    . In the Attributes window in the Title field set the button's title to
    "Push". +
    How the application should look: +
    image:formatted%20pictures/HowWindowShouldLook.png[How the window should
    look,width=357,height=156] +
     +
    . Create a new empty file called main.go.
    . Save this file in the same folder as the xib file. 
    . Copy and paste this code into the file: +
     +
    // File: main.go +
    // Description: Uses a NIB file to display the interface +
    // Run directions: go run main.go +
     +
    package main +
     +
    import ( +
        "fmt" +
        "os" +
        "github.com/progrium/macdriver/macos/appkit" +
        f "github.com/progrium/macdriver/macos/foundation" +
        "github.com/progrium/macdriver/objc" +
        "github.com/progrium/macdriver/helper/action" +
        "unsafe" +
    ) +
     +
    // used by both main() and doButton() +
    var textField appkit.TextField +
     +
    func main() \{ +
        // setup the application +
        app := appkit.Application_SharedApplication() +
        app.SetActivationPolicy(appkit.ApplicationActivationPolicyRegular) +
        app.ActivateIgnoringOtherApps(true) +
            +
        // get the nib file's data +
        godata, err := os.ReadFile("MainMenu.nib") +
        handleError(err != nil, "Failed to load nib file") +
        myNib := appkit.NewNibWithNibDataBundle(godata, nil) +
        +
        // obtain all root level objects from the nib file +
        var myObjects = f.Array_Array() +
        status := myNib.InstantiateWithOwnerTopLevelObjects(nil,
    unsafe.Pointer(&myObjects)) +
        handleError(status == false, "Failed to instantiate nib file") +
     +
        // find the window and display it +
        // assumes there is only one window object at the root level +
        var i uint +
        var mainWindow appkit.Window +
        for i = 0; i < myObjects.Count(); i++ \{ +
            theObject := myObjects.ObjectAtIndex(i) +
            if theObject.IsKindOfClass(objc.GetClass("NSWindow")) \{ +
                mainWindow = appkit.WindowFrom(theObject.Ptr()) +
                mainWindow.OrderFront(nil) +
                break +
            } +
        } +
     +
        // if the window was not found +
        handleError(mainWindow.IsNil(), "Failed to find the window
    object") +
     +
        // setup the button +
        aButton := mainWindow.ContentView().ViewWithTag(1) +
        handleError(aButton.IsNil(), "Failed to find the button object") +
        action.Set(appkit.ButtonFrom(aButton.Ptr()), doButton) +
     +
        // setup the textfield +
        textFieldView := mainWindow.ContentView().ViewWithTag(2) +
        handleError(textFieldView.IsNil(), "Failed to find the textfield
    object") +
        textField = appkit.TextFieldFrom(textFieldView.Ptr()) +
     +
        // start the main loop +
        app.Run() +
    } +
     +
    // handles any error situations +
    func handleError(isProblem bool, errorMessage string) \{ +
        if isProblem == true \{ +
            panic(errorMessage) +
        } +
    } +
     +
    // called when the user pushes the button +
    func doButton(sender objc.Object) \{ +
        fmt.Println("button pushed") +
        textField.SetStringValue("You pushed the button") +
    } +
     +
     +
    . Open the Terminal application.
    . cd to the folder that contains all the project files. +
    . Run this command to setup the program: +
    go mod init program
    . Then open the file go.mod and add these lines to the end of the
    file: +
    require github.com/progrium/macdriver v0.5.0  +
    replace github.com/progrium/macdriver => <path to your darwinkit
    folder>  +
    . Fill in the path of your local repo in the last line. Be sure there is
    at least one space between the '=>' and the start of your path.
    . Convert the MainMenu.xib file to a nib file using this command: +
    ibtool --compile MainMenu.nib MainMenu.xib
    . Now you will run this program using this command: +
    go run main.go

Congratulations! You have finished making your application.

The end result


[.underline]#
Issues

#If you see this error while using the ibtool:
xcode-select: error: tool 'ibtool' requires Xcode, but active developer directory '/Library/Developer/CommandLineTools' is a command line tools instance

Run this command to fix this problem:
sudo xcode-select -s /Applications/Xcode.app/Contents/Developer


[.underline]

How the Program Works

The root level of the nib file contains four items, File’s Owner, First Responder, Application, and the window we created. The Nib method InstantiateWithOwnerTopLevelObjects() is used to get all the root level objects and place them into an array. The loop in the main() function is used to look thru each object in the array. If it finds an object that is of type NSWindow then we have found the window we created.

The button and the textfield are looked up by tag number, bound to local variables, and then setup for use.

Clone this wiki locally