let's build a macos app without xcode!
I don't like using Xcode and I wanted to see if I could start building a native MacOS app without it. Turns out it's really easy with Swift!
what you need
- Swift, via Xcode: I installed Xcode from the App Store; you do need it installed for the SwiftUI and other libraries)
- An IDE, probably: I used VSCode with the official Swift extension
step 1
In a new folder, run:
swift package init --name "Hello World" --type empty
Honestly you could skip to step 2 since all this does is create the following minimal Package.swift:
import PackageDescription
let package = Package(
name: "Hello World"
)
step 2
Update Package.swift with the platform we're targeting, macOS, and an executable target name (this will be the name of the compiled binary).
let package = Package(
name: "Hello World",
platforms: [.macOS(.v15)],
targets: [
.executableTarget(
name: "Hello World",
)
]
)
step 3
mkdir Sources
Time to add some code! Swift looks at Sources for source code by default. Interestingly, it doesn't care about your folder layout inside of Sources at all.
step 4: finally, some code
Create Sources/App.swift. We're using SwiftUI to render our app so we import SwiftUI (setting platforms above enables this import).
It doesn't really matter what this file is named or where it's located in Sources, Swift knows to look for the @main annotation.
import SwiftUI
@main
struct HelloWorldApp: App {
var body: some Scene {
Window("Hello World", id: "main") {
Text("Hello, world!")
}
.defaultSize(width: 400, height: 400)
}
}
step 5: run it!
That's all you need! Just two files, Package.swift and Sources/App.swift. Now you can start your app by running:
swift run
it's not an ".app" app
This creates an app but it's not a real app in the sense of being a bundled MacOS app with a dock icon and all, to say nothing of being an app we can distribute to others.
I'm still figuring out how to make it a real app and it's a lot more complex - what I know so far is that it involves creating an app bundle, and then running the gauntlet of setting up your Apple developer account, creating/downloading certificates/provisioning profiles, codesigning, and notarization.
step 6: I still want a dock icon
After reading this thread I realized that to get a dock icon my app could activate itself as a regular app with NSApp. I used an AppDelegate to hook into the lifecycle of my app to do this.
This says app delegates are no longer recommended, and it seems when I figure out how to make a real bundled app, it'll be the default and I won't need to configure it manually.
import SwiftUI
@main
struct HelloWorldApp: App {
@NSApplicationDelegateAdaptor(AppDelegate.self) var appDelegate
var body: some Scene {
Window("Hello World", id: "main") {
Text("Hello, world!")
}
.defaultSize(width: 400, height: 400)
}
}
class AppDelegate: NSObject, NSApplicationDelegate {
func applicationDidFinishLaunching(_ notification: Notification) {
NSApp.setActivationPolicy(.regular)
NSApp.activate()
}
}
this is a start
Despite the long road ahead I'm excited that I can start playing with native macOS apps and familiarizing myself with SwiftUI! It feels empowering to have a real UI and to be able to talk to real system APIs.
My next step is to build a small app to manage the Apple developer certificates and provisioning profiles on my Mac, and try to get that through the process.