Display window on OSX using Swift without XCode or NIB

Disclaimer: I am trying the next exercise because I think it will be instructive. I am wondering how this can be done. So please don't be in a rush to jump in with "This is the wrong way to do it, you should never do that!"

Working from the command line using my favorite text editor, I would like to create a minimal Swift program that displays a window.

It's a welcoming GUI / Cococa world if you will.

In the same vein, I want to avoid NIB.

So, No XCode, No NIB.

I would like to:

  • compile it with a fast compiler
  • create #! swift script that is executed using the Swift interpreter

If I can do both of these things, I will feel my feet on the ground and upgrade to Xcode much more easily.

I tried the following:

window.swift

import Cocoa

@NSApplicationMain
class AppDelegate: NSObject, NSApplicationDelegate {
    @IBOutlet weak var window: NSWindow!
    let newWindow = NSWindow(contentRect : NSScreen.mainScreen()!.frame
                             , styleMask : NSBorderlessWindowMask
                             ,   backing : NSBackingStoreType.Buffered 
                             ,     defer : false)
    func applicationDidFinishLaunching(aNotification: NSNotification) {
        // Insert code here to initialize your application
        newWindow.opaque = false
        newWindow.movableByWindowBackground = true
        newWindow.backgroundColor = NSColor.whiteColor()
        newWindow.makeKeyAndOrderFront(nil)
    }
    func applicationWillTerminate(aNotification: NSNotification) {
        // Insert code here to tear down your application
    }
}

      

However, trying to do this from the command line failed:

pi@piBookAir.local ~ /Users/pi/dev/macdev:
 ‐  swift window.swift 
window.swift:3:1: error: 'NSApplicationMain' attribute cannot be used in a 
                         module that contains top-level code
@NSApplicationMain
^
window.swift:1:1: note: top-level code defined in this source file
import Cocoa
^
 ✘

      

What's the correct way to resolve the error?

+3


source to share


3 answers


Make TestView.swift file (for example):

    import AppKit

    class TestView: NSView
    {
            override init(frame: NSRect)
            {
                    super.init(frame: frame)
            }

            required init?(coder: NSCoder)
            {
                    fatalError("init(coder:) has not been implemented")
            }

            var colorgreen = NSColor.greenColor()

            override func drawRect(rect: NSRect)
            {
                    colorgreen.setFill()
                    NSRectFill(self.bounds)

                    let h = rect.height
                    let w = rect.width
                    let color:NSColor = NSColor.yellowColor()

                    let drect = NSRect(x: (w * 0.25),y: (h * 0.25),width: (w * 0.5),height: (h * 0.5))
                    let bpath:NSBezierPath = NSBezierPath(rect: drect)

                    color.set()
                    bpath.stroke()

                    NSLog("drawRect has updated the view")
            }
    }

      

Make TestApplicationController.swift file (for example):

    import AppKit

    final class TestApplicationController: NSObject, NSApplicationDelegate
    {

            ///     Seems fine to create AppKit UI classes before `NSApplication` object
            ///     to be created starting OSX 10.10. (it was an error in OSX 10.9)
            let     window1 =       NSWindow()
            let     view1   =       TestView(frame: NSRect(x: 0, y: 0, width: 1000, height: 1000))

            func applicationDidFinishLaunching(aNotification: NSNotification)
            {
                    window1.setFrame(CGRect(x: 0, y: 0, width: 1000, height: 1000), display: true)
                    window1.contentView =   view1
                    window1.opaque      =   false
                    window1.center();
                    window1.makeKeyAndOrderFront(self)
            //      window1.backgroundColor = view1.colorgreen
            //      window1.displayIfNeeded()
            }

            func applicationWillTerminate(aNotification: NSNotification) {
                    // Insert code here to tear down your application
            }
    }

      

Make a main.swift file (for example):

    //
    //  main.swift
    //  CollectionView
    //
    //  Created by Hoon H. on 2015/01/18.
    //  Copyright (c) 2015 Eonil. All rights reserved.
    //

    import AppKit

    let     app1            =       NSApplication.sharedApplication()
    let     con1            =       TestApplicationController()

    app1.delegate   =       con1
    app1.run()

      

The last file shouldn't be renamed, main.swift seems to be a special name for swift (otherwise the example won't compile).

Now enter this (to compile the example):



swiftc -sdk /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/TestView.swift TestApplicationController.swift main.swift

      

Run the code by entering:

./main

      

It shows the main window (centered) with a nice green column and a yellow rectangle inside it). You can kill the application by entering Control-C.

Please note that this is fast compilation, not an interpreter, so you have your own application.

Note: -sdk and the path to MacOSX10.11.sdk are required (otherwise the code will not compile).

Please note that this compilation depends on the latest Xcode distribution, so update MacOSX10.11.sdk to MacOSX10.10.sdk or whatever in the SDK directory.

It took a while to find out ...

+8


source


Porting this code from objective-c to Swift, you get

import Cocoa

let nsapp = NSApplication.shared()
NSApp.setActivationPolicy(NSApplicationActivationPolicy.regular)
let menubar = NSMenu()
let appMenuItem = NSMenuItem()
menubar.addItem(appMenuItem)
NSApp.mainMenu = menubar
let appMenu = NSMenu()
let appName = ProcessInfo.processInfo.processName
let quitTitle = "Quit " + appName
let quitMenuItem = NSMenuItem.init(title:quitTitle,
  action:#selector(NSApplication.terminate),keyEquivalent:"q")
appMenu.addItem(quitMenuItem);
appMenuItem.submenu = appMenu;
let window = NSWindow.init(contentRect:NSMakeRect(0, 0, 200, 200),
    styleMask:NSTitledWindowMask,backing:NSBackingStoreType.buffered,defer:false)
window.cascadeTopLeft(from:NSMakePoint(20,20))
window.title = appName;
window.makeKeyAndOrderFront(nil)
NSApp.activate(ignoringOtherApps:true)
NSApp.run()

      

Save it as a minimal .swift, compile

swiftc minimal.swift -o minimal

      



and run

./minimal

      

An empty window and menu bar appears with a menu named as application and an exit button.

Why it works exactly, I don't know. I'm new to Swift and Cocoa programming, but the linked website explains a bit.

+3


source


Pass the flag -parse-as-library

to swiftc

:

swiftc -parse-as-library window.swift

      

However, you just end up with the second error when you do this:

2015-06-10 14: 17: 47.093 window [11700: 10854517] No Info.plist file in app bundle or no NSPrincipalClass file in Info.plist file coming out

0


source







All Articles