Объедините значения отправки свойства @Published, если они не обновлены - PullRequest
0 голосов
/ 06 мая 2020

Я пытался создать динамическую c форму с использованием SwiftUI и Combine, которая загружает параметры ввода (в примере number) на основе другого ввода (в примере myString).

Проблема в том, что стек Combine выполняется непрерывно, делая множество сетевых запросов (в примере, имитируемых задержкой), даже если значение никогда не менялось.

Я думаю, что ожидаемое поведение таково, что $myString публикует значения только при их изменении.

class MyModel: ObservableObject {

    // My first choice on the form
    @Published var myString: String = "Jhon"

    // My choice that depends on myString
    @Published var number: Int?

    var updatedImagesPublisher: AnyPublisher<Int, Never> {
        return $myString
            .removeDuplicates()
            .print()
            .flatMap { newImageType in
                return Future<Int, Never> { promise in

                    print("Executing...")

                    // Simulate network request
                    DispatchQueue.main.asyncAfter(deadline: .now() + 2.0) {
                        let newNumber = Int.random(in: 1...200)
                        return promise(.success(newNumber))
                    }
                }
        }
        .receive(on: DispatchQueue.main)
        .eraseToAnyPublisher()
    }
}

struct ContentView: View {

    @ObservedObject var model: MyModel = MyModel()

    var body: some View {
        Text("\(model.number ?? -100)")
            .onReceive(model.updatedImagesPublisher) { newNumber in
                self.model.number = newNumber
            }
    }
}

Ответы [ 2 ]

3 голосов
/ 06 мая 2020

Проблема в том, что updatedImagesPublisher является вычисляемым свойством. Это означает, что вы создаете новый экземпляр каждый раз, когда получаете к нему доступ. Что происходит в вашем коде. Объект Text подписывается на updatedImagesPublisher, когда он получает новое значение, он обновляет свойство number модели. number - это свойство @Published, это означает, что метод objectWillChange будет вызываться каждый раз, когда вы его измените, и тело будет воссоздано. Новый Text подпишется на новый updatedImagesPublisher (потому что это вычисляемое свойство) и снова получит значение. Чтобы избежать такого поведения, просто используйте свойство lazy вместо свойства computed.

lazy var updatedImagesPublisher: AnyPublisher<Int, Never> = {
    return $myString
        .removeDuplicates()
        .print()
        .flatMap { newImageType in
            return Future<Int, Never> { promise in

                print("Executing...")

                // Simulate network request
                DispatchQueue.main.asyncAfter(deadline: .now() + 2.0) {
                    let newNumber = Int.random(in: 1...200)
                    return promise(.success(newNumber))
                }
            }
    }
    .receive(on: DispatchQueue.main)
    .eraseToAnyPublisher()
}()
1 голос
/ 06 мая 2020

Я полагаю, это потому, что вы создаете нового издателя для каждого обновления представления, попробуйте вместо этого следующее. (Протестировано с Xcode 11.4)

class MyModel: ObservableObject {

    // My first choice on the form
    @Published var myString: String = "Jhon"

    // My choice that depends on myString
    @Published var number: Int?

    lazy var updatedImagesPublisher: AnyPublisher<Int, Never> = {
        return $myString
            .removeDuplicates()
            .print()
            .flatMap { newImageType in
                return Future<Int, Never> { promise in

                    print("Executing...")

                    // Simulate network request
                    DispatchQueue.main.asyncAfter(deadline: .now() + 2.0) {
                        let newNumber = Int.random(in: 1...200)
                        return promise(.success(newNumber))
                    }
                }
        }
        .receive(on: DispatchQueue.main)
        .eraseToAnyPublisher()
    }()
}
...