Reputation: 5085
I am getting an infinite loop, onReceive is called infinitely when using a protocol with @Published variable. To use @Published variable in a protocol I am using this stackoverflow answer. This line in MyImageView:
if self.viewModel.answered {
seems to cause a call in
onReceive { self.viewModel.objectWillChange
inside ContentView.
Any idea how to make this work?
This is the entire code:
EnvObject:
import UIKit
import SwiftUI
import Combine
import Foundation
protocol MyProtocol: AnyObject {
var answered: Bool { get set }
var answeredPublished: Published<Bool> { get }
var answeredPublisher: Published<Bool>.Publisher { get }
}
class GameViewModel: ObservableObject & MyProtocol {
@Published var answered = false
var answeredPublished: Published<Bool> { _answered }
var answeredPublisher: Published<Bool>.Publisher { $answered }
}
Views:
import SwiftUI
struct MyImageView: View {
@EnvironmentObject var viewModel: GameViewModel
var body: some View {
if self.viewModel.answered {
print("not nil")
return AnyView(Image("clearImage")
.resizable()
.scaledToFit()
)
}
else {
print("nil")
return AnyView(
Text(self.viewModel.answered ? "true" : "false")
)
}
}
}
struct ViewBelow<ViewModel: ObservableObject & MyProtocol>: View {
@EnvironmentObject var viewModel: ViewModel
var body: some View {
Text("below")
.onReceive(self.viewModel.objectWillChange,
perform: { _ in
print("onReceive target")
}
)
}
}
struct ContentView: View {
@EnvironmentObject var viewModel: GameViewModel
var body: some View {
GeometryReader {
geo in
VStack {
MyImageView()
ViewBelow<GameViewModel>()
}
}
}
}
SceneDelegate import UIKit import SwiftUI
class SceneDelegate: UIResponder, UIWindowSceneDelegate {
var window: UIWindow?
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
let contentView = ContentView().environmentObject(GameViewModel())
if let windowScene = scene as? UIWindowScene {
let window = UIWindow(windowScene: windowScene)
window.rootViewController = UIHostingController(rootView: contentView)
self.window = window
window.makeKeyAndVisible()
}
}
}
Upvotes: 2
Views: 1404
Reputation: 5085
This worked for me:
class MyPublishedProperties {
@Published var answered = false
}
protocol MyProtocol: MyPublishedProperties {
func changePublishedPropertyValue(newValue: Bool)
}
class GameViewModel: ObservableObject & MyPublishedProperties, MyProtocol {
func changePublishedPropertyValue(newValue: Bool) {
answered = newValue
}
}
Upvotes: 1