4

I'm trying to learn SwiftUI by rewriting an old AppKit-based editor of mine. I'm currently stuck on something that is (fairly) straightforward using AppKit but seems almost impossible in SwiftUI (given my limited understanding of the latter).

What I'm trying to accomplish is a document-based app with tabbed views for the documents and a navigation sidebar to browse files (similar to e.g. Xcode).

The following is a minimal example of what I have come up with:

struct ContentView: View {
    @Binding var document: FileBrowserTest4Document
    let fileURL: URL?

    var body: some View {
        BrowserView(placeholder: fileURL?.absoluteString ?? "no file")
    }
}

struct BrowserView: View {
    let placeholder: String
    @State private var root = FileItem(url: URL.documentsDirectory.appendingPathComponent("Test"))
    
    var body: some View {
        NavigationSplitView {
            List([root], children: \.children) { item in
                Button(action: { print("\(item.url)") }) {
                    Label(item.segment, systemImage: item.isFolder ? "folder" : "document")
                        .labelStyle(.titleAndIcon)
                }
                .buttonStyle(.plain)
            }
            HStack {
                Button("Help") { print("Help") }
                Button("Add…") { print("Add…") }
            }
        } detail: {
            Text(placeholder)
        }
    }
}

where FileItem is basically a wrapper around URL not relevant to the question:

struct FileItem: Identifiable, Hashable {
    let url: URL
    
    ...
}

The result is shown below (with the documents created by New and Open…, respectively.

  • tabbed documents: check
  • sidebar: check
  • sidebar togggle button: check

However, the file tree in the browser is tied to the document instance and not "global" to the app. This is somewhat expected from the view hierarchy above, but not what I want. I've tried several approaches with e.g. HStack with NavigationStack + TextView, but no combination seem to achieve what I want. Searching the web turns up nothing helpful except solutions based on wrapping AppKit components. I feel like I'm in a a maze of twisty little passages, all alike…

Is what I want to do possible using SwiftUI? Any pointers, suggestions or definitive "No" would be greatly appreciated.

Update 251003:
Trying to clarify what I want to achieve (still exploring, not set in stone):

  • double clicking a file in the navbar would open it (or bring to front an already open file) in a tab
  • clicking Add… button (bottom of navbar) would add a new file in the current directory (given by the selection in file tree)
  • Similarly, renaming/deleting files
  • Add/Delete/Rename could be contextual menu
  • Selecting a tab would highlight the corresponding entry in the file tree
  • In practice, every document would have an associated rootDirectory determining the root of the file tree

FWIW, I've been using TextMate for most of my editing for the past 20 years so I might be slightly one-eyed.

Update 251003-2:
Just pointing out that

NSDocumentController.shared.openDocument(
       withContentsOf: item.url, 
              display: true, 
    completionHandler: {_,_,_ in print("Opened \(item.url)") }
)

as button action in the NavigationSplitView List would solve the first bullet above, but I guess that that counts as falling back to AppKit?

14
  • But the problem with Xcode is its not possible to drag a tab off and make a new window like we can with DocumentGroup. Maybe you could store the state of the sidebar in the project's config file, then it could be synced between all the sidebars? So despite being different UI objects, they all look the same, and then that would continue to work if a tab was dragged off into a new window and you would actually need multiple UI objects for the sidebar. Commented Oct 2 at 14:55
  • Hmm, I hadn't considered Xcode's behaviour as a problem until now:) but what you say makes a lot of sense, particularly if the sidebar reflects the contents of the document (e.g. sectioning in a long .tex file) rather than just the file structure. I thought of the sidebar as a convenient substitute for the File-menu, and providing an overview of the files I'm working on. Still, I think it would be a good learning experience to strong-arm SwiftUI to mimic the behaviour of Xcode. Commented Oct 2 at 17:29
  • SwiftUI is a declarative UI framework: you describe what the UI should look like and how it reacts to data. So when you say, However, the file tree in the browser is tied to the document instance and not global to the app… but not what I want, that is not quite correct. What you have is simply how your data root is displayed in the view as a file tree. The data itself can be made global easily. What you don’t want is to make the view global—that’s not how SwiftUI works. That’s how AppKit works, which is totally different. Commented Oct 2 at 22:51
  • 1
    No intention of pushing to AppStore; target audience will build from source (should there ever be an audience). As for HIG, they are just that: guidelines. IMHO personal productivity and workflow beats general guidelines every time;) Commented Oct 3 at 11:50
  • 1
    Please break questions like this up into multiple smaller questions in the future. Commented Oct 3 at 15:57

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.