Skip to content

How to use nib files with DarwinKit

programmingkidx edited this page Feb 7, 2024 · 3 revisions


Assumptions made in this tutorial

  • Have Xcode installed
  • Have DarwinKit installed
  • Running Mac OS


System Requirements

  • Xcode version: unknown
  • Mac OS version: unknown
  • Go version: 1.18 or higher
  • DarwinKit: commit 6a98f82290e53bc5d82cb59436644f705156d7cb or higher


Do you like to program in Go?
Do you want to make programs for Mac OS?
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:

  1. Start Xcode.
  2. Create a new empty XIB file by going to File -> New -> macOS-> User Interface -> Empty.
    NewXIB

  3. Push the Next button and save the file as MainMenu.xib.
  4. Show the library by going to View->Show Library.
    Show Library

  5. In the search field at the top enter "window".
    Searching for a window

  6. Drag this window object to the middle of the window while holding down the Option key.
    Adding a
window
    Note: Holding down the Option key initially makes the Library window stay open for subsequent drags.

  7. Next search for a push button.
  8. Drag this control to the window.
  9. Then find a textfield and drag it onto the window.
  10. You may close the Library window now if you wish.
  11. Click on the textfield to select it.
  12. Click on this button to show the Attributes Inspector.
    Attribute Inspector button

  13. In the Attributes window scroll down to the View section.
  14. In the Tag field enter 1.
    Setting the tag

  15. Do the same thing for the push button except enter 2 this time.
  16. Click on the window's title bar.
    Window's
ttilebar
  17. 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".
    Set the
window title

  18. Click on the button.

  19. In the Attributes window in the Title field set the button's title to "Push".
    How
the window should look

  20. Create a new empty file called main.go.
  21. Save this file in the same folder as the xib file. 
  22. 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")
    }
    
    
  23. Open the Terminal application.
  24. cd to the folder that contains all the project files.
  25. Run this command to setup the program:
    go mod init program
  26. 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> 
  27. 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.
  28. Convert the MainMenu.xib file to a nib file using this command:
    ibtool --compile MainMenu.nib MainMenu.xib
  29. Now you will run this program using this command:
    go run main.go
Congratulations! You have finished making your application.

The end result

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

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.