alamodey
alamodey

Reputation: 14953

Updating a menubar title for MacOS

I'm trying to create a menubar app that will update the word shown in the menubar every 10 minutes. But at the moment it is just showing KBar initially instead of being set to a random word immediately:

import SwiftUI

@main
struct KBarApp: App {
    var database = Database()
    @State private var heading = "KBar"  // Initial title in the menu bar
    
    // Function to update the title every 10 minutes
    func startTimer() {
        Timer.scheduledTimer(withTimeInterval: 600, repeats: true) { _ in
            let word = database.randomWord()  // Always returns a word
            heading = word.korean  // Update heading to the new word
            print("Timer updated heading: \(heading)")  // Debug: print the new heading
        }
    }
    
    // Initialize the database and fetch a random word when the app starts
    init() {
        database = Database()  // Initialize the database
        let word = database.randomWord()  // Get initial random word
        heading = word.korean  // Set the initial heading
        print("Initial heading: \(heading)")  // Debug: print initial heading
    }

    var body: some Scene {
        MenuBarExtra(LocalizedStringKey(heading)) {
            ContentView()
                .onAppear {
                    // Start the timer when the app starts
                    startTimer()
                    print("App appeared. Initial heading: \(heading)")  // Debug: print when app starts
                }
        }
        .menuBarExtraStyle(.window)
    }
}

Upvotes: 1

Views: 41

Answers (1)

Sweeper
Sweeper

Reputation: 273540

You should not set @State properties in the init of your App. SwiftUI is not yet ready to handle @State changes in this point yet.

The onAppear attached to ContentView will also not be called until after the user opens the menu bar extra. That's the first time ContentView appears.

If you pass a label: { ... } argument to the MenuBarExtra instead of just a LocalizedStringKey, you can write Text(heading).onAppear { ... } , and this onAppear will be called.


Here is an example of the menu bar extra title changing every one second. I have used a task instead of a Timer.

let words = ["Foo", "Bar", "Baz", "Apple", "Orange", "Peach"]
@State var title = "Foo"
MenuBarExtra {
    ContentView()
} label: {
    Text(title)
        .task {
            do {
                while true {
                    title = words.randomElement()!
                    try await Task.sleep(for: .seconds(1))
                }
            } catch {
                // task has been cancelled
            }
        }
}
.menuBarExtraStyle(.window)

Upvotes: 0

Related Questions