Использование Combine для настройки издателей, а также удовлетворения требований к начальным значениям переменных, не равных нулю - PullRequest
0 голосов
/ 10 апреля 2020

Я относительно новичок в концепциях реактивного программирования, и я пытаюсь создать простую модель представления для обновления @Published Bool значений, которые используются для обновления пользовательского интерфейса с помощью SwiftUI.

Эта конкретная модель установка этих значений Bool на основе других значений из структуры WatchConnectivity при их изменении с течением времени.

Несмотря на то, что это простой пример, и он работает, я чувствую, что упускаю возможности уменьшить избыточность.

В частности, странно, что я повторяю логи c, используемые для установки начальных значений appNotInstalled и complicationNotInstalled позже, когда я использую Publishers.CombineLatest и Publishers.CombineLatest3.

Несмотря на то, что начальные значения передаются через издателей, поэтому они go через конвейеры CombineLatest и устанавливают начальные значения, кажется неправильным произвольно устанавливать опубликованные переменные на true или false , но компилятор заставляет меня устанавливать для них начальные значения где-то.

Если я не устанавливаю начальные значения, я получаю ошибку Variable 'self.appNotInstalled' used before being initialized.

Есть ли ва? y Я могу избежать установки начальных значений, не делая их равными nil, или другим способом избежать дублирования логики c, используемой для определения их значений?

Вот рабочий код, который у меня есть:

class WatchConnectivityModel: ObservableObject {

    // values used to show/hide UI
    @Published var appNotInstalled: Bool
    @Published var complicationNotInstalled: Bool

    private var cancellables: [AnyCancellable] = []

    init() {

        // initialize based on the values of everything at class init
        let activated = WCSession.default.activationState == .activated
        let appInstalled = WCSession.default.isWatchAppInstalled
        let complicationInstalled = WCSession.default.isComplicationEnabled
        appNotInstalled = !(activated && appInstalled)
        complicationNotInstalled = activated && appInstalled && !complicationInstalled

        // set up the publishers for any changes
        let activationStatePublisher = WCSession.default.publisher(for: \.activationState)
        let isWatchAppInstalledPublisher = WCSession.default
            .publisher(for: \.isWatchAppInstalled)
        let isComplicationEnabledPublisher = WCSession.default
            .publisher(for: \.isComplicationEnabled)

        // set up assignment of appNotInstalled for changes
        Publishers.CombineLatest(activationStatePublisher.removeDuplicates(),
                                 isWatchAppInstalledPublisher.removeDuplicates())
            .map { (state, installed) in
                // repeated logic from above
            return !(state == .activated && installed)
        }.receive(on: RunLoop.main)
            .assign(to: \.appNotInstalled, on: self)
            .store(in: &cancellables)

        // set up assignment of complicationNotInstalled for changes
        Publishers.CombineLatest3(activationStatePublisher.removeDuplicates(),
                                  isWatchAppInstalledPublisher.removeDuplicates(),
                                  isComplicationEnabledPublisher.removeDuplicates())
            .map { (state, appInstalled, complicationInstalled) in
                // repeated logic again
                return state == .activated && appInstalled && !complicationInstalled
        }.receive(on: RunLoop.main)
            .assign(to: \.complicationNotInstalled, on: self)
            .store(in: &cancellables)
    }
}

1 Ответ

0 голосов
/ 18 апреля 2020

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

import Foundation
import Combine
import WatchConnectivity

class WatchConnectivityModel: ObservableObject {

    // values used to show/hide UI
    @Published var appNotInstalled: Bool
    @Published var complicationNotInstalled: Bool

    private struct AppNotInstalled {

        let value: Bool

        init(_ activationState: WCSessionActivationState,
             _ appInstalled: Bool) {
            value = !(activationState == .activated && appInstalled)
        }
    }

    private struct ComplicationNotInstalled {

        let value: Bool

        init(_ activationState: WCSessionActivationState,
             _ appInstalled: Bool,
             _ complicationInstalled: Bool) {
            value = activationState == .activated && appInstalled && !complicationInstalled
        }
    }

    private var cancellables: [AnyCancellable] = []

    init() {

        // initilize based on the current values
        let state = WCSession.default.activationState
        let appInstalled = WCSession.default.isWatchAppInstalled
        let complicationInstalled = WCSession.default.isComplicationEnabled

        appNotInstalled = AppNotInstalled(state,
                                          appInstalled).value
        complicationNotInstalled = ComplicationNotInstalled(state,
                                                            appInstalled,
                                                            complicationInstalled).value

        // set up the publishers
        let activationStatePublisher = WCSession.default
            .publisher(for: \.activationState)
        let isWatchAppInstalledPublisher = WCSession.default
            .publisher(for: \.isWatchAppInstalled)
        let isComplicationEnabledPublisher = WCSession.default
            .publisher(for: \.isComplicationEnabled)

        // set up assignment of appNotInstalled
        Publishers.CombineLatest(activationStatePublisher.removeDuplicates(),
                                 isWatchAppInstalledPublisher.removeDuplicates())
            .map { (state, installed) in
                return AppNotInstalled(state, installed).value
        }.receive(on: RunLoop.main)
            .assign(to: \.appNotInstalled,
                    on: self)
            .store(in: &cancellables)

        // set up assignment of complicationNotInstalled
        Publishers.CombineLatest3(activationStatePublisher.removeDuplicates(),
                                  isWatchAppInstalledPublisher.removeDuplicates(),
                                  isComplicationEnabledPublisher.removeDuplicates())
            .map { (state, appInstalled, complicationInstalled) in
                return ComplicationNotInstalled(state, appInstalled, complicationInstalled).value
        }.receive(on: RunLoop.main)
            .assign(to: \.complicationNotInstalled,
                    on: self)
            .store(in: &cancellables)
    }
}
...