Journal

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.

2022-11-18 Example of hierarchical list

I actually didn't know that the SwiftUI List element natively supports hierarchical lists, until someone on Reddit asked for an example. Here's mine:

    struct Park: Identifiable {
        let id = UUID()
        let name: String
        let children: [Park]?
    }
    struct ContentView: View {
        private let parks = [
            Park(name: "Sierra Nevada", children: [
                Park(name: "Yosemite", children: [
                    Park(name: "Mount Dana", children: nil),
                    Park(name: "Mount Lyell", children: nil),
                ]),
                Park(name: "Stanislaus National Forest", children: [
                    Park(name: "Emigrant Wilderniss", children: nil),
                    Park(name: "Carson-Iceberg Wilderniss", children: nil),
                ])
            ])
        ]
        var body: some View {
            List(parks, children: \.children, rowContent: { Text($0.name) })
        }
    }

2022-09-15 Xcode linker error ld framework not found

Today I got the following error when trying to build my unittest for my Swift package:

  ld: framework not found CoreAPIKit

Note that the name of that particular framework (CoreAPIKit) doesn't matter; it's a framework that's only used internally at my client. Now on to the solution... In Xcode, we selected the project, and selected the unittest target, then selected the Build Phases tab. In that tab, there's the Link Binary With Libraries setting.

That setting listed "CoreAPIKit.framework"! So, why does this go wrong?

We removed that framework, then added it again. This time, it was saying "CoreAPIKit" (i.e. without the extension). Now, the unittest target correctly linked.

More...