Journal

2023-03-28 Contacts app macOS sorting

The Contacts app (the app previously known as Address Book) will sort by default on last name. If you prefer sorting on first name, and you're a command-line oriented person like me, you want to set your sorting preference from a script. Here's how:

    defaults write com.apple.AddressBook ABNameSortingFormat -string "sortingFirstName sortingLastName"

2023-02-22 Unselectable picker in SwiftUI

Today I spent too much time looking at a bit of code that wasn't working. Here's the problem: the following picker uses a struct to both display values, as well as save the selection. Only problem is; it doesn't work. It displays correctly, but you can't select it; essentially it's read-only.

    struct UnselectablePicker: View {
        struct Language: Identifiable, Hashable  {
            var title: String
            var id: String
        }
        
        var languages: [Language] = [
            Language(title: "English", id: "en-US"),
            Language(title: "German", id: "de-DE"),
            Language(title: "Korean", id: "ko-KR"),
            Language(title: "Dutch", id: "nl-NL")
        ]
        @State private var selectedLanguage = UnselectablePicker.Language(title: "German", id: "de-DE")
        
        var body: some View {
            Picker(selection: $selectedLanguage, label: Text("Front Description")) {
                ForEach(self.languages) {
                    Text($0.title)
                }
            }.pickerStyle(.segmented)
        }
    }

Did you spot it? The problem is in the ForEach. The picker works with the following correction:

    ForEach(self.languages, id: \.self) {
        // ...
    }

Alternatively, add a tag.

    ForEach(self.languages) {
        Text($0.title)
            .tag($0.title)
    }

2023-01-24 Moving server

Today, I'm moving this website to a new server with a new Debian version. This may mean some interruptions.

Update: the move was a success. The new provider is AlphaVPS and I hope I'll be set for a couple of years.

https://alphavps.com/

2022-12-12 When to use StateObject wrappedValue initializer

UPDATE 2023-04-28: Apple updated its documentation: https://developer.apple.com/documentation/swiftui/stateobject#Initialize-state-objects-using-external-data

On our team, we recently discussed the use of StateObject in SwiftUI. It's very useful for ViewModels. However you often want to initialize such a ViewModel with one or more parameters and you bump into the following bit of documentation:

"You don't call this initializer directly. Instead, declare a property with the @StateObject attribute in a View, App, or Scene, and provide an initial value"

Source: developer.apple.com

We were not the only ones struggling with this; see also this blog post by Sarun W, where they share a Slack screenshot quoting an Apple engineer saying that this initializer is supported.

Still, using that initializer means that if your view gets recreated, you lose all state since the ViewModel is destroyed and created as well. This is especially harrowing when you do network actions within the ViewModel.

Our conclusion is that StateObject(wrappedValue:) can be used at points in your app where you start a completely new flow. For instance in a popup where you create a screen with a NavigationView or NavigationStack. Otherwise, initialize elsewhere and use @EnvironmentObject.

More...