Home > database >  Swift UI - How to access a @State variable externally?
Swift UI - How to access a @State variable externally?

Time:09-23

I have ContentView.swift

struct ContentView: View {
    @State var seconds: String = "60"

    var body: some View {
        TextField("60", text: $seconds)
    }
    
    func getSeconds() -> String {
        return $seconds.wrappedValue;
    }
}

And AppDelegate.swift

class AppDelegate: NSObject, NSApplicationDelegate {
    var contentView = ContentView()

    ...
    
    func launchTimer() {
        print(contentView.getSeconds())
        let timer = DispatchTimeInterval.seconds(Int(contentView.getSeconds()) ?? 0)
        
        DispatchQueue.main.asyncAfter(deadline: .now()   timer) {
            self.launchTimer()
        }
    }
    
    ...

    @objc func showPreferenceWindow(_ sender: Any?) {
        if(window != nil) {
            window.close()
        }
        
        window = NSWindow(
            contentRect: NSRect(x: 0, y: 0, width: 480, height: 300),
            styleMask: [.titled, .closable, .miniaturizable, .resizable, .fullSizeContentView],
            backing: .buffered, defer: false)
        window.isReleasedWhenClosed = false
        window.contentView = NSHostingView(rootView: contentView)
        
        window.center()
        window.makeKeyAndOrderFront(nil)
        NSApplication.shared.activate(ignoringOtherApps: true)
    }
}

contentView.getSeconds() always returns 60, even if I modify the value of the TexField in my content view (by manually typing in the text field).

How does one get the wrapped value/real value of a state variable from the app delegate?

CodePudding user response:

add a variable to appDelegate

var seconds : Int? 

then do this in your contentView :

    .onChange(of: seconds) { seconds in
        guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else {return}
        appDelegate.seconds = seconds
    }

CodePudding user response:

Ended up instantiating the ContentView with the AppDelegate as a param. I added a seconds attribute to the AppDelegate to be modified by the ContentView.

AppDelegate.swift

import SwiftUI

@NSApplicationMain
class AppDelegate: NSObject, NSApplicationDelegate {
    var contentView: ContentView?
    var seconds = 60
    
    func applicationDidFinishLaunching(_ aNotification: Notification) {
        self.contentView = ContentView(self)        

        launchTimer()
    }
    
    func launchTimer() {
        let timer = DispatchTimeInterval.seconds(seconds)
        
        print(timer)
        
        DispatchQueue.main.asyncAfter(deadline: .now()   timer) {
            self.launchTimer()
        }
    }
}

ContentView.swift

import SwiftUI

struct ContentView: View {
    @State var seconds: String = "60"
    var appDelegate: AppDelegate
    
    init(_ appDelegate: AppDelegate) {
        self.appDelegate = appDelegate
    }

    var body: some View {
        TextField("60", text: $seconds, onCommit: {
            self.appDelegate.seconds = Int($seconds.wrappedValue) ?? 0
         })
    }
}

This code hurts my brain and surely there is a better way of doing this

  • Related