Weblog entries 2023

2023-12-01 Unable to use public key authentication for ssh login on Raspberry Pi

TL;DR: Raspberry Pi OS will reject rsa keys, use another type of key, and copy it to the Pi:

  % ssh-keygen -t ecdsa
  % ssh-copy-id .ssh/id_ecdsa.pub <pi-hostname>

Explanation is as follows. Recently I installed a Raspberry Pi 4, and I flashed the SD card with the Raspberry Pi Imager. I used the default OS (which is titled "Raspberry Pi OS, a port of Debian Bookworm"). I wanted to use passwordless login to ssh, i.e. public key authentication. So I copied my existing public key to the Raspberry Pi with ssh-copy-id. However when accessing the Pi over ssh, I still had to enter my password.

This had me stumped for a while. In the end, I turned on debug logging:

  % sudo vim /etc/ssh/sshd_config

Then add the following line:

  LogLevel DEBUG3

Restart SSH daemon and follow logs:

  % sudo systemctl restart sshd
  % journalctl -f

Try and log in with your old RSA key, and you'll see the following log message:

  Dec 01 09:27:53 HL46528028 sshd[2025]: debug3: mm_answer_keyallowed: publickey authentication test: RSA key is not allowed

What you need to do, is generate a new key with a different type:

  % ssh-keygen -t ecdsa

The default is to save they keypair in the ~/.ssh directory and call it id_ecdsa and id_ecdsa.pub. Copy the public key to the Raspberry:

  % ssh-copy-id .ssh/id_ecdsa.pub <pi-hostname>

Now enjoy passwordless login:

  % ssh <pi-hostname>

Of course don't forget to remove the LogLevel line from the sshd configuration, and restart the daemon.

2023-09-18 SwiftUI Separate toolbar

In SwiftUI, it's easy to just keep coding and dump everything in one view. Toolbars especially are "bulky", they take up a lot of lines, and are not regular views so syntax-wise, they're a bit of a bother to separate away. Here's an example of a bottom toolbar, to help you split up those big views.

    struct MainView: View {
        var body: some View {
            Color.blue
                .ignoresSafeArea(edges: [.top, .leading, .trailing])
                .toolbar {
                    ActionToolbar()
                }
        }
    }
    struct ActionToolbar: ToolbarContent {
        var body: some ToolbarContent {
            ToolbarItem(placement: .bottomBar) {
                 Spacer()
             }
            ToolbarItem(placement: .bottomBar) {
                Button(action: { print("Plus") }) {
                    Image(systemName: "plus.app.fill")
                        .resizable()
                        .scaledToFit()
                }
            }
        }
    }

2023-08-30 Coordinator in preview

When you're using the Coordinator pattern in a SwiftUI project, you'll find yourself sometimes wanting to preview the initial start of a "flow" of screens. But that's quite a bit of work because inside the coordinator, some boilerplate code needs to be present, to wrap the SwiftUI view in a UIHostingController.

This bit of code is useful to plunk in the utilities folder of your project, and use in the preview section of your SwiftUI view.

    private class PreviewViewController: UIViewController {
        private let coordinator: NavigationControllerCoordinator
        init(coordinator: NavigationControllerCoordinator) {
            self.coordinator = coordinator
            super.init(nibName: nil, bundle: nil)
        }
        required init?(coder: NSCoder) {
            fatalError()
        }
        override func viewDidLoad() {
            guard let navigationController = self.navigationController as? NavigationController else {
                return
            }
            title = "Preview"
            var configuration = UIButton.Configuration.filled()
            configuration.title = "Start"
            self.view = UIButton(configuration: configuration, primaryAction: UIAction(handler: {_ in
                navigationController.present(self.coordinator.navigationController, animated: true)
                self.coordinator.start()
            }))
        }
    }
    struct PreviewCoordinator: UIViewControllerRepresentable {
        let coordinator: NavigationControllerCoordinator
        typealias UIViewControllerType = NavigationController
        func makeUIViewController(context: Context) -> NavigationController {
            let viewController = PreviewViewController(coordinator: coordinator)
            return NavigationController(rootViewController: viewController)
        }
        func updateUIViewController(_ uiViewController: NavigationController, context: Context) {}
    }

Use as follows;

    struct NetworkScanQRView_Previews: PreviewProvider {
        static var previews: some View {
            PreviewCoordinator(coordinator: NetworkScanQRCoordinator.mocked)
        }
    }

2023-06-14 SwiftUI pet peeves

This is my list of SwiftUI pet peeves. It's a work in progress.

  • Using .onTapGesture when you should actually use a button. Although it's nicely concise, a button will show visual feedback, and can easily be adjusted for accessibility.
  • Using a class when a struct suffices.
  • When naming a function that instantiates something, it should be prefixed with "make", not with "get" or something else. See also: https://www.swift.org/documentation/api-design-guidelines/
  • Hardcoded button sizes, when they actually should be a ScaledMetric.

2023-04-12 Natural breaks in Time Out application for macOS

I'm using a break reminder on macOS, it's called Time Out. https://dejal.com/timeout/

It's a very full-featured app, one of them is that it can skip breaks if it detects a natural break. Meaning, if you go and get a cup of coffee, it counts as a break.

The problem was, that this wasn't working. I found out how to fix this, and I'm documenting it here. Firstly, to see if the app is unable to detect the computer idle, open Time Out, and in the left navigation, select "Advanced". Then turn on "Output scheduler logging" and click the "Open Console" button. This will open Console, the macOS log viewer.

In Console, click the blue Start Streaming button, then in the top right, type: "process: Time Out" and press enter. You should now only see output from Time Out. If you don't move the keyboard or mouse, Time Out should detect that you leave the computer idle. In my case that wasn't happening, it just repeatedly logged the following lines, edited for brevity:

    idle (for 0 secs); Normal due 12/04/2023, 11:19 (in 11 min, 46 secs); .......
    idle (for 0 secs); Normal due 12/04/2023, 11:19 (in 11 min, 45 secs); .......
    idle (for 0 secs); Normal due 12/04/2023, 11:19 (in 11 min, 44 secs); .......

When I went back to Time Out, and in the Advanced screen, changed "Natural break detection method" from "Event Monitor" to "Event Source", then back to "Event Monitor", this fixed the problem! It should now detect idling correctly:

    idle (for 0 secs); Normal due ... not yet idle for at least 30 seconds
    idle (for 1 secs); Normal due ... not yet idle for at least 30 seconds
    idle (for 2 secs); Normal due ... not yet idle for at least 30 seconds

Note that perhaps on the same screen (Advanced), you'll have to adjust the "Natural break active threshold" as well, mine is set to 30 seconds. After the problem is fixed, be sure to disable "Output scheduler logging" as well.

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/