Normal view

There are new articles available, click to refresh the page.
Before yesterdayMain stream

Last Week on My Mac: Who’s afraid of changing interface?

By: hoakley
11 May 2025 at 15:00

With Apple’s annual Worldwide Developers Conference less than a month away, speculation about what’s coming in macOS 16 is starting to warm up. So far that has concentrated on increasing consistency in interfaces across the different platforms, which could mean almost anything. As far as macOS is concerned, that’s largely up to AppKit and SwiftUI, its two major interface libraries.

AppKit remains widely used, and is still the more complete of the two. Descended from the UI framework in NeXTSTEP, it was in the core of Mac OS X at the start, and has been the mainstay for the Finder and Apple’s own apps for the last 25 years. It has a close relative in UIKit for iOS and iPadOS, although they are less comprehensive in their features.

SwiftUI is an interesting experience for the macOS developer, and is currently an archipelago of delights in a sea of disappointment. Some of its features are powerful, but a great deal is still lacking. Support for views and features widely used in modern iOS and iPadOS apps is impressive, and it opens up features such as the List View that I praised recently. But when it comes to essentials that are confined to macOS, such as menus, a great deal of work remains as it comes up to its sixth birthday on 3 June.

This is demonstrated in one of the best tutorials I’ve seen on using SwiftUI for macOS, in this case to develop a Markdown editor, written by Sam Rowlands of Ohanaware. No sooner has he set up a split view to accommodate both the Markdown source and its preview in the same window, than he writes: “The TextEditor in SwiftUI ticks the box of offering a way to edit a large volume of text, but that’s about all it does. Apple have a much more powerful text editor already in the macOS as part of their AppKit framework, so we’re going to wrap that instead.”

This was my experience a while ago when I looked at a range of document formats. Open the Help book in LogUI and what you see there is cast not in SwiftUI, which still doesn’t offer a PDF view, but reaches back to AppKit. While creating a useful Rich Text editor using AppKit is amazingly quick and simple, even plain text editing in SwiftUI is feeble. There are plenty of experts who will advise you “SwiftUI’s text editor is very limited. It doesn’t support much more than entering large amounts of plain text. If you want rich text editing, you will have to use either NSTextView or UITextView.”

These are fundamental features that should by now be easy going for any macOS interface library that’s six years old.

Continuing dependence on both AppKit and SwiftUI presents Apple with the problem of having to update both to reflect changes it intends making to improve interface consistency, then for iOS there’s also UIKit. Not only that, but all three libraries have to integrate and work together.

SwiftUI has undergone constant change over those six years. One of its most substantial changes has been the move from the Observable Object protocol to the Observable macro. Apple describes how to migrate in this article, complete with sample code. But that poses the developer a problem, as adopting the latter is only possible in apps written for macOS 14 or iOS 17 or later. That’s why LogUI requires a minimum of macOS 14.6, as do many of the better SwiftUI apps. Writing SwiftUI apps to support macOS older than Sonoma and Sequoia is thus a serious undertaking, and whatever macOS 16 and its new version of SwiftUI bring, you can be sure they’ll make backward compatibility even more impractical.

Documentation for SwiftUI is also broken. Apple seems to have stopped writing conceptual explanations about ten years ago, and structured guides have been replaced by terse and usually uninformative references to individual functions and other details of the macOS API. The only way to try to gain understanding of SwiftUI is to turn to third-parties, who are more interested in the lucrative iOS market rather than macOS, and think a series of example projects are a substitute for a systematic guide.

If there’s one sound investment for the future that Apple could make from its much-vaunted half trillion dollar investment in the US, it would be to hire a large team of technical authors and catch up with its ten-year backlog of documentation.

Whether you gasp with horror or delight when Apple reveals what’s coming in macOS 16 next month, spare a thought for all the changes that have to take place in AppKit, UIKit and SwiftUI, all the documentation that won’t get written, and how code is going to struggle to be compatible with macOS 16 and Sequoia or earlier. Then for good measure throw in the inevitable load of new bugs. So you still want to beta-test macOS 16?

Why I like SwiftUI List Views

By: hoakley
18 April 2025 at 14:30

Presenting complex verbal information in an accessible way remains one of the great challenges in interface design. This article explains how SwiftUI List Views offer solutions that are superior to those provided in AppKit, and why I have chosen to use them in several of my recent apps, such as LogUI and AppexIndexer.

Each of the apps I discuss here has a common problem: it has to display a series of text records consisting of fields whose contents can be completely empty or expand into long paragraphs. Thus the condensed length of each row, representing a single record, can be as short as 30 characters, or as long as hundreds, some with embedded line-breaks. Fields contain contrasting data, such as datestamps, flowing text and UUIDs. The user needs to scan rows rapidly, following patterns in fields and their contents, and to read each carefully.

There are some ground rules, notably:

  • rows must remain visually separate,
  • the order of fields in each row must be the same,
  • each field must be displayed in full and not truncated,
  • fields must be visually distinct across each row,
  • field width must be adjusted automatically to avoid horizontal scrolling.

There are centuries of experience in print design of tables, to which computers add dynamic resizing of columns and ready use of colour.

tuneperf2

In some circumstances, AppKit’s Table View works well, here in Activity Monitor. However, adjustable column widths can’t overcome one problem shown here, where uniform column width wastes space for short process names, and truncates others.

logui00

This shows how poorly a Table View copes with log entries in Console.

ulbow1b103

My best solution using AppKit is for rows of untruncated text, with colour used to distinguish fields within them. Unfortunately, some rows inevitably overflow into multiple lines, and may require very wide windows to remain accessible.

This is SwiftUI’s List View in action with a log excerpt in LogUI. Although it might appear desirable to allow manual adjustment of field width, it’s more practical to provide options to change which fields are displayed, and so accommodate narrower windows. Sometimes line breaking in fields isn’t good, but I think this is a problem of content (computing terms being formed by concatenating series of words) rather than view design.

This is AppexIndexer, where I use an easily distinguished emoji to mark a significant field positioned in the middle of some rows. This aids navigation when scanning rows quickly, but doesn’t rely on the reading of the emoji.

unhidden1

Here’s another example, this time using many icons drawn from SF Symbols rather than pure text. This works well with fewer rows, but rendering many of those icons in thousands of rows may not be as efficient.

For macOS, SwiftUI continues to suffer serious omissions, and in some circumstances isn’t yet a good fit. But its List Views are a compelling reason for using SwiftUI in many native Mac apps. Further details are given in the references below, and the Appendix provides example source code for a basic implementation in SwiftUI.

Apple Developer Documentation

SwiftUI List view
SwiftUI Table view
AppKit NSTableView

Appendix: Example source code

struct ContentView: View {
    @State private var messageInfo = Logger()
    
    var body: some View {
        let _ = messageInfo.getMessages()
        if (self.messageInfo.logList.count > 0) {
            VStack {
                List(self.messageInfo.logList) { line in
                MessageRow(line: line)
                }
            }
            .frame(minWidth: 900, minHeight: 200, alignment: .center)
        } else {
            Text("No results returned from the log for your query.")
                .font(.largeTitle)
        }
    }
}

struct MessageRow: View {
    let line: LogEntry

    var body: some View {
        HStack {
            Text(line.date + "  ")
            if #available(macOS 14.0, *) {
                Text("\(line.type)")
                    .foregroundStyle(.red)
            } else {
                Text("\(line.type)")
                    .foregroundColor(.red)
            }
            if !line.activityIdentifier.isEmpty {
                Text(line.activityIdentifier)
            }
//          etc.
            Text(line.composedMessage)
            }
        }
    }

❌
❌