Swiftui: Why Doesn't Observedobject Work in Appdelegate

SwiftUI: Why doesn't ObservedObject work in AppDelegate?

When you create a subscription with .sink you have to save the AnyCancellable object returned

let cancellable = publisher.sink { ... }

And if you assign it to a variable, make sure it is not short lived. As soon as the cancellable object gets deallocated, the subscription will get cancelled too.

SwiftUI macOs AppDelegate detect the change of ObservedObject

There is a more sophisticated way to observe UserDefaults.

The Combine framework provides a publisher for UserDefaults, which is actually a Key-Value Observing Publisher

First make your backgroundIsTransparent property an extension of UserDefaults

extension UserDefaults {
@objc var backgroundIsTransparent: Bool {
get {
guard object(forKey: PreferencesKeys.backgroundIsTransparent) != nil else { return true }
return bool(forKey: PreferencesKeys.backgroundIsTransparent)
}
set {
set(newValue, forKey: PreferencesKeys.backgroundIsTransparent)
}
}
}

In AppDelegate import Combine and create a Set for the subscription(s)

import Combine

@main
class AppDelegate: NSObject, NSApplicationDelegate { ...

private var subscriptions = Set<AnyCancellable>()

and in applicationDidFinishLaunching add the publisher

    func applicationDidFinishLaunching(_ aNotification: Notification) {

// .....

UserDefaults.standard.publisher(for: \.backgroundIsTransparent)
.sink { state in
print(state)
// do something with state
}
.store(in: &subscriptions)
}

An alternative using your approach is to make userPreferences a @StateObject

@StateObject var userPreferences = UserPreferences.instance

and observe backgroundIsTransparent

userPreferences.$backgroundIsTransparent
.sink { state in
print(state)
// do something with state
}
.store(in: &subscriptions)

How does $ work in SwiftUI and why can I cast a @StateObject to an @ObservedObject

@StateObject and @ObservedObject are sources of truth, they are very similar to each other and almost interchangeable.

To share these with other Views you should pass it to the AddFoodView using .environmentObject(fridge) and change the @ObservedObject var fridgeView: Fridge; in AddFoodView to @EnvironmentObject var fridgeView: Fridge.

Look up the Apple Documentation on "Managing Model Data in Your App".

struct BookReader: App {
@StateObject var library = Library()

var body: some Scene {
WindowGroup {
LibraryView()
.environmentObject(library)
}
}
}

struct LibraryView: View {
@EnvironmentObject var library: Library

// ...
}

Another good source are the Apple SwiftUI Tutorials, especially "Handling User Input"

SwiftUI doesn't update state to @ObservedObject cameraViewModel object

The problem with your provided code is that the type of selectedFocus within the FocusPicker view should be Integer rather than Float. So one option is to change this type to Integer and find a way to express the AVCaptureDevice.lensPosition as an Integer with the given range.

The second option is to replace the feets array with an enumeration. By making the enumeration conform to the CustomStringConvertible protocol, you can even provide a proper description. Please see my example below.

I've stripped your code a bit as you just wanted to display the number in the first step and thus the code is more comprehensible.

My working example:

import SwiftUI
import Combine

struct ContentView: View {
@ObservedObject var cameraViewModel = CameraViewModel(focusLensPosition: 0.5)

var body: some View {
VStack {
ZStack {
VStack {
FocusPicker(selectedFocus: $cameraViewModel.focusLensPosition)

Text(String(self.cameraViewModel.focusLensPosition))
.foregroundColor(.red)
.font(.largeTitle)
}
.frame(maxWidth: .infinity, alignment: .leading)
}
.edgesIgnoringSafeArea(.all)
}
}
}

struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}

class CameraViewModel: ObservableObject {
@Published var focusLensPosition: Float

init(focusLensPosition: Float) {
self.focusLensPosition = focusLensPosition
}
}

enum Feets: Float, CustomStringConvertible, CaseIterable, Identifiable {
case case1 = 0.0
case case2 = 0.5
case case3 = 1.0

var id: Float { self.rawValue }
var description: String {
get {
switch self {
case .case1:
return "∞ ft"
case .case2:
return "4"
case .case3:
return "Auto"
}
}
}
}

struct FocusPicker: View {
@Binding var selectedFocus: Float

var body: some View {
Picker(selection: $selectedFocus, label: Text("")) {
ForEach(Feets.allCases) { feet in
Text(feet.description)
}
.animation(.none)
.background(Color.clear)
.pickerStyle(WheelPickerStyle())
}
.frame(width: 60, height: 200)
.border(Color.gray, width: 5)
.clipped()
}
}

SwiftUI: Access @EnvironmentObject from AppDelegate

An @EnvironmentObject doesn't need to be instantiated directly in your SwiftUI objects; rather, it can be allocated somewhere else (for example, your UISceneDelegate) and then passed through using the .environment(…) function.

You could also allocate it on your AppDelegate, and pass that object though to your views in UISceneDelegate.scene(_:willConectTo:options:) method.

Paul Hudson has a good description of all of this at https://www.hackingwithswift.com/quick-start/swiftui/how-to-use-environmentobject-to-share-data-between-views

iOS SwiftUI: ObservableObject from parent to child

Instead of passing the same model to each subview, you can use @EnvironmentObject instead which is specifically designed for this.

You essentially have to inject your ObservableObject in your parent view with something like this (typically in your appdelegate before presenting the parent view, or in your previews setup for live previewing):

Parent().environmentObject(ViewModel())

then you can automatically get a reference in each view with something like this:

struct AnyChild: View {
@EnvironmentObject var model: ViewModel
//...
}

and use it with your bindings

Here is a brief article about the use of environment

Finally, regarding why your solution is not working, you must consider a single source of truth (the observable object in your Parent) and a @Binding for each subview that will eventually change the state of this object. I hope that this makes sense...

Xcode SwiftUI window not shown after successful build

This might be due to a bug.

I removed all the other schemes but the one I was using in the project and the window is now loading.

Yet, the same warning is still there.



Related Topics



Leave a reply



Submit