Не удается создать программную привязку Какао в Swift - PullRequest
0 голосов
/ 11 марта 2019

Я разрабатываю приложение для macOS, используя swift.В моем приложении есть объект Alarm, который можно включить или отключить.Я создал привязку Какао из свойства enable будильника к флажку в пользовательском интерфейсе.Чтобы проверить, работает ли привязка, я добавил оператор print в метод наблюдателя свойства didSet состояния включенного Alarm, и это сообщение распечатывается при переключении флажка.

Теперь я хочу написать модульный тест дляубедитесь, что при переключении свойства enabled действительно выполняются правильные действия, и у меня возникают трудности с программным созданием привязки в модульном тесте, которая будет имитировать то, что происходит с привязкой, созданной в Interface Builder.

Тест выглядит как-токак это:

func testAlarmTogglesEnabledState() {
    let alarm = Alarm()
    let enabler = AlarmEnabler(alarm)

    enabler.enable()

    XCTAssertTrue(alarm.enabled)
}

Тест на самом деле более сложный, чем этот, так как аварийный сигнал на самом деле предпринимает некоторые действия, чтобы включить или отключить сам себя, однако, это, мы надеемся, служит в основном для иллюстрации того, что происходит.

Класс AlarmEnabled выглядит следующим образом:

@objc private class AlarmEnabler: NSObject {
    init(_ alarm: Alarm) {
        super.init()
        let name = NSBindingName(rawValue: "enabled")
        bind(name, to: alarm, withKeyPath: "enabled", options: nil)
    }

    func enable() {
        enabled = true
    }

    @objc dynamic var enabled = false
}

И класс Alarm имеет такую ​​форму:

@objc class Alarm: NSObject {
    …

    @objc private(set) var enabled = true {
        didSet {
            print("Alarm's enabled state was set to \(enabled)")
        }
    }

    …
}

Сообщение в наблюдателе свойства didSet никогда не печатается при запуске теста, так что программная привязка явно не работает.

Вот игровая площадка, которую я пытался написать, которая в основном выполняет то, что пытается выполнить модульный тест:

import Cocoa

@objc class Alarm: NSObject, Codable {
    init(_ enabled: Bool = true) {
        self.enabled = enabled
    }

    @objc dynamic var enabled: Bool {
        didSet {
            print("Alarm's enabled state was set to \(enabled)")
        }
    }
}

@objc class AlarmEnabler: NSObject {
    init(_ alarm: Alarm) {
        self.alarm = alarm
        super.init()
        let name = NSBindingName(rawValue: #keyPath(AlarmEnabler.enabled))
        bind(name, to: alarm, withKeyPath: #keyPath(Alarm.enabled), options: nil)
//      bind(name, to: alarm, withKeyPath: #keyPath(AlarmEnabler.enabled), options: nil)
        checkBox.bind(NSBindingName.value, to: alarm, withKeyPath: #keyPath(Alarm.enabled), options: nil)
    }

    deinit {

    }

    func enable() {
        enabled = true
        checkBox.state = .on
    }

    func disable() {
        enabled = false
        checkBox.state = .off
    }

    @objc func checkBoxToggled(_ sender: NSButton) {
    }

    let checkBox = NSButton(checkboxWithTitle: "", target: self, action: #selector(AlarmEnabler.checkBoxToggled))
    @objc dynamic var enabled = false
    let alarm: Alarm
}

let alarm = Alarm(false)
let enabler = AlarmEnabler(alarm)

enabler.enable()
enabler.disable()

Она правильно связывает сигнал тревоги, а также пытается имитировать то, что происходит в пользовательском интерфейсе,используя флажок.Кажется, что ни один из них не может заставить собственность измениться.Что может заставить код игровой площадки работать правильно?

1 Ответ

0 голосов
/ 20 марта 2019

Хорошо, благодаря Willeke, я смог создать игровую площадку, которая работает, и я также смог написать модульный тест и заставить его пройти. Вот версия игровой площадки, которая будет работать правильно:

import Cocoa

@objc class Alarm: NSObject, Codable {
    init(_ enabled: Bool = true) {
        self.enabled = enabled
    }

    @objc private(set) var enabled: Bool {
        didSet {
            print("Alarm's enabled state was set to \(enabled)")
        }
    }
}

@objc class AlarmEnabler: NSObject {
    init(_ alarm: Alarm) {
        super.init()
        checkBox.bind(NSBindingName.value, to: alarm, withKeyPath: #keyPath(Alarm.enabled), options: nil)
    }

    func enable() {
        checkBox.state = .on
        checkBox.sendAction(checkBox.action, to: checkBox.target)
    }

    func disable() {
        checkBox.state = .off
        checkBox.sendAction(checkBox.action, to: checkBox.target)
    }

    @objc func checkBoxToggled(_ sender: NSButton) {
    }

    let checkBox = NSButton(checkboxWithTitle: "", target: self, action: #selector(AlarmEnabler.checkBoxToggled))
}

let alarm = Alarm(false)
let enabler = AlarmEnabler(alarm)

enabler.enable()
enabler.disable()
...