Looming Deadlines – Devlog 002

Starting over with a new Xcode Project, SwiftData setup, CloudKit syncing, and opening windows.

I spent a couple of hours working with the prototype project from last year. I pulled the immersive space and all the code for 3D text. Then I added macOS as one of the supported platforms and was blown away by the number of visionOS-only features I was using. Things like pushWindow, ornaments, toolbar placement. I think I had around 60 reasons why the app would not build for macOS. I considered fixing each of these issues with platform conditionals, but that seemed like a sure path to unmanageable code.

I decided it was time to a fresh start. I created a new Xcode project yesterday. I started with SwifUi multi-platform project template. I also selected SwiftData checked the boxes to sync the data with CloudKit. This gave me a single window with a list of items and a button to add records. I made some very minimal changes to the Item model.

import Foundation
import SwiftData

@Model
final class Item {
    var id = UUID()
    var creationTimestamp: Date = Date()

    var title: String = ""
    var deadline_date: Date = Date()
    var status: String = "Active" // TODO: make an enum for status values

    init(title: String, deadline_date: Date, status: String) {

        self.title = title
        self.deadline_date = deadline_date
        self.status = status
    }
}

Used the existing content view to cobble together a few things. A list of items hosted in a NavigationSplitView. A custom (and very temporary) row that will navigate to a detail view. On visionOS and macOS, this will display as a two-column UI, but on iOS it will collapse into a stack. I borrowed the countdown views that I made last summer and added a mesh gradient as a background for now.

The main NavigationSplitView on all three platforms.

This got me far enough that could add records on each device and see them showing up on the others. Success! I had a little more time so I went to see if I could get floating windows working on macOS. I recently read an article by Natalia Panferova that described how to make one those minimal macOS windows. The only thing I had to add to her example was .windowLevel(.floating).

My macOS window. I still need to work on the sizing for this, but it was good enough for today.

        WindowGroup(id: "MacFloating", for: PersistentIdentifier.self, content: { $id in
            SingleRoot(id: $id)
                .modelContainer(sharedModelContainer)
                .frame(width: 680, height: 300)
                .toolbar(removing: .title)
                .toolbarBackground(.hidden, for: .windowToolbar)
        })
        .windowManagerRole(.associated)
        .windowLevel(.floating)
        .windowResizability(.contentSize)
        .windowBackgroundDragBehavior(.enabled)

My visionOS window just uses .defaultSize and passes in the shared model.

        WindowGroup(id: "VisionPopout", for: PersistentIdentifier.self, content: { $id in
            SingleRoot(id: $id)
                .modelContainer(sharedModelContainer)
        })
        .defaultSize(CGSize(width: 680, height: 300))

Watch this video to see the floating macOS window vs. a regular system window in visionOS.

Next up, I’ll start thinking about the customization that I want to provide and how to model them into data that I can store and sync with SwiftData.

Questions or feedback?