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.
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() } } } }
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) } }
This is my list of SwiftUI pet peeves. It's a work in progress.
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.
If you need an introduction to Kafka, here's a very nice YouTube video on the subject: https://www.youtube.com/watch?v=Ch5VhJzaoaI
Note that it's 4 years old, and I'm not sure how fast Kafka moves.
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"
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) }
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.