Ошибка инициализации класса при добавлении в него ObservedObject

Хочу реализовать горячие кнопки для проекта с помощью NSEvent дублирующие основные Button.

При привязке class ViewController - @ObservedObject var answers: Answers , class ViewController пишет ошибку:

Class 'ViewController' has no initializers

В примере пока только горячие кнопки для одного объекта (Эта часть работает).

В чем может быть проблема с инициализацией?

import SwiftUI
import Carbon

class Answer: ObservableObject, Identifiable {
    let id = UUID()
    @Published var answerYes: Bool?
    
    init(answerYes: Bool?) {
        self.answerYes = answerYes
    }
}

class Answers: ObservableObject {
    @Published var items = [Answer]()
    
    init(_ items: [Answer]) {
        self.items = items
    }
}

class ViewController: NSViewController {  
    
   @ObservedObject var answers: Answers
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        NSEvent.addLocalMonitorForEvents(matching: .keyDown) {
            self.keyDown(with: $0)
            return nil
        }
    }
    override func keyDown(with event: NSEvent) {
        
        if event.keyCode == 18 {
            // answers.items[0].answerYes = true
            print("1")
        }
        if event.keyCode == 19 {
            // answers.items[0].answerYes = false
            print("2")
        }
    }
}
var viewControlle = ViewController()

struct ContentView: View {
    
    @ObservedObject var answers: Answers
    
    var body: some View {
        HStack {
            ForEach(answers.items) { item in
                GameView(answer: item)
            }
        }
        .frame(width: 600, height: 400, alignment: .center)
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView(answers: Answers([
            Answer(answerYes: nil),
            Answer(answerYes: nil),
            Answer(answerYes: nil),
            Answer(answerYes: nil)
        ]))
    }
}

struct GameView: View {
    
    @ObservedObject var answer: Answer
    
    var body: some View {
        VStack {
            // buttons
            HStack {
                HStack {
                    Button("Yes") {
                        answer.answerYes = true
                    } // Выводит Yes в первом поле
                    Button("No") {
                        answer.answerYes = false
                    } // Выводит No в первом поле
                }
            }
            // circle
            ZStack {
                Circle()
                    .fill(Color.yellow)
                    .frame(width: 100, height: 100)
                    .shadow(color: .black, radius: 2, x: 3, y: 3)
                Text(answer.answerYes == nil ? "" : answer.answerYes! ? "yes" : "no")
                    .foregroundColor(Color(#colorLiteral(red: 0.8039215686, green: 0.5215686275, blue: 0.09803921569, alpha: 1)))
                    .font(.system(size: 40))
                    .offset(y: -5)
                    .opacity(answer.answerYes == nil ? 0 : 1)
            }
        }
    }
}

Ответы (2 шт):

Автор решения: schmidt9

Вопрос легко гуглится по запросу "swift viewcontroller has no initializers", суть в том, что переменная answers у вас не инициализирована, исправить можно так

@ObservedObject var answers = Answers([])

либо к примеру так

    override init(nibName nibNameOrNil: NSNib.Name?, bundle nibBundleOrNil: Bundle?) {
        answers = Answers([])
        super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
    }
    
    required init?(coder: NSCoder) {
        answers = Answers([])
        super.init(coder: coder)
    }

Дополнение

Отлов нажатий лучше вынести в отдельный класс-обработчик (вью контроллер тут не нужен), который можно привязать к каждому объекту Answer как показано в примере, при этом остается открытым вопрос, как определять в какой круг посылать нажатия, очевидно еще нужен механизм определения выбранного круга с вопросом

import Foundation
import SwiftUI

struct AnswerKeysHandler {
    
    @ObservedObject var answer: Answer
    
    init(answer: Answer) {
        self.answer = answer
        registerKeysHandler()
    }
    
    private func registerKeysHandler() {
        NSEvent.addLocalMonitorForEvents(matching: .keyDown) {
            self.keyDown(with: $0)
            return nil
        }
    }
    
    private func keyDown(with event: NSEvent) {
        
        switch event.keyCode {
        case 18:
            answer.answerYes = true
            print("1")
        case 19:
            answer.answerYes = false
            print("2")
        default:
            print("key pressed with code \(event.keyCode)")
        }
        
    }
    
}

Использование на примере первого вопроса

@main
class AppDelegate: NSObject, NSApplicationDelegate {
    
    var window: NSWindow!
    var handler: AnswerKeysHandler!
    
    func applicationDidFinishLaunching(_ aNotification: Notification) {
        // Create the SwiftUI view that provides the window contents.
        let answer1 = Answer(answerYes: nil)
        let contentView = ContentView(answers: Answers([
            answer1,
            Answer(answerYes: nil),
            Answer(answerYes: nil),
            Answer(answerYes: nil)
        ]))
        
        handler = AnswerKeysHandler(answer: answer1)

        // Create the window and set the content view.
        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.center()
        window.setFrameAutosaveName("Main Window")
        window.contentView = NSHostingView(rootView: contentView)
        window.makeKeyAndOrderFront(nil)
    }
    
}

Дополнение 2 - разрешение ввода текста в текстовых полях

    private func registerKeysHandler() {
        NSEvent.addLocalMonitorForEvents(matching: .keyDown) {
            self.keyDown(with: $0)
            return $0 // <--- передаем событие дальше для обработки
        }
    }
→ Ссылка
Автор решения: Mayskiy

Этот код очень хорошо отслеживает нажатие кнопок клавиатуры:

    class ViewController: NSViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        
        NSEvent.addLocalMonitorForEvents(matching: .keyDown) {
            self.keyDown(with: $0)
            return nil
        }
    }
    override func keyDown(with event: NSEvent) {
        
        if event.keyCode == 18 {
            //print(answers.items)
            //answers.items[1].answerYes = true
            print("1")
        }
        if event.keyCode == 19 {
          //  answers.items[0].answerYes = false
            print("2")
        }
    }
}
var viewControlle = ViewController()                                                            
Помещаю его в переменную viewControlle

И в файле AppDeligate добавляю:

func applicationDidFinishLaunching(_ aNotification: Notification) {
            // Create the SwiftUI view that provides the window contents.
            let contentView = ContentView(answers: Answers([
                Answer(answerYes: nil),
                Answer(answerYes: nil),
                Answer(answerYes: nil),
                Answer(answerYes: nil)
            ]))
            
            viewControlle.viewDidLoad()
            
            // Create the window and set the content view.
            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.center()
            window.setFrameAutosaveName("Main Window")
            window.contentView = NSHostingView(rootView: contentView)
            window.makeKeyAndOrderFront(nil)
        }
    
        func applicationWillTerminate(_ aNotification: Notification) {
            // Insert code here to tear down your application
        }
    
    
    }
→ Ссылка