स्विफ्ट कॉम्बिनेशन में @Published का उपयोग करके गणना किए गए गुणों के बराबर?


20

अनिवार्य स्विफ्ट में, राज्य की नकल के बिना डेटा तक सुविधाजनक पहुंच प्रदान करने के लिए गणना की गई संपत्तियों का उपयोग करना आम है।

मान लीजिए कि मेरे पास अनिवार्य MVC उपयोग के लिए बनाया गया यह वर्ग है:

class ImperativeUserManager {
    private(set) var currentUser: User? {
        didSet {
            if oldValue != currentUser {
                NotificationCenter.default.post(name: NSNotification.Name("userStateDidChange"), object: nil)
                // Observers that receive this notification might then check either currentUser or userIsLoggedIn for the latest state
            }
        }
    }

    var userIsLoggedIn: Bool {
        currentUser != nil
    }

    // ...
}

अगर मैं Combine के साथ एक प्रतिक्रियाशील समतुल्य बनाना चाहता हूं, उदाहरण के लिए SwiftUI के साथ उपयोग करने के लिए, मैं आसानी @Publishedसे संग्रहीत संपत्तियों को जोड़ सकता हूँ Publishers उत्पन्न करने के लिए , लेकिन संगणित गुणों के लिए नहीं।

    @Published var userIsLoggedIn: Bool { // Error: Property wrapper cannot be applied to a computed property
        currentUser != nil
    }

ऐसे विभिन्न कार्यदक्ष हैं, जिनके बारे में मैं सोच सकता था। मैं अपनी गणना की हुई संपत्ति को इसके बदले संग्रहीत कर सकता था और उसे अद्यतन रख सकता था।

विकल्प 1: एक संपत्ति पर्यवेक्षक का उपयोग करना:

class ReactiveUserManager1: ObservableObject {
    @Published private(set) var currentUser: User? {
        didSet {
            userIsLoggedIn = currentUser != nil
        }
    }

    @Published private(set) var userIsLoggedIn: Bool = false

    // ...
}

विकल्प 2: Subscriberमेरी अपनी कक्षा में उपयोग करना :

class ReactiveUserManager2: ObservableObject {
    @Published private(set) var currentUser: User?
    @Published private(set) var userIsLoggedIn: Bool = false

    private var subscribers = Set<AnyCancellable>()

    init() {
        $currentUser
            .map { $0 != nil }
            .assign(to: \.userIsLoggedIn, on: self)
            .store(in: &subscribers)
    }

    // ...
}

हालांकि, ये वर्कअराउंड गणना गुणों के रूप में सुरुचिपूर्ण नहीं हैं। वे राज्य की नकल करते हैं और वे एक साथ दोनों गुणों को अपडेट नहीं करते हैं।

Publisherकंबाइन में एक कम्प्यूटेड संपत्ति में एक जोड़ने के लिए एक उचित समकक्ष क्या होगा ?



1
कंप्यूटेड प्रॉपर्टीज ऐसे गुण हैं जो व्युत्पन्न प्रॉपर्टीज हैं। उनके मूल्य निर्भर के मूल्यों पर निर्भर करते हैं। अकेले इस कारण से, यह कहा जा सकता है कि वे कभी भी अभिनय की तरह नहीं होते हैं ObservableObject। आप स्वाभाविक रूप से मान लेते हैं कि किसी ObservableObjectवस्तु में परिवर्तन करने की क्षमता होनी चाहिए, जो कि परिभाषा के अनुसार, कम्प्यूटेड संपत्ति के मामले में नहीं है ।
नईम

क्या आपको इसका हल मिला? मैं ठीक उसी स्थिति में हूँ, मैं से बचने के राज्य करना चाहते हैं और अभी भी प्रकाशित कर सकेंगे
erotsppa

जवाबों:


2

डाउनस्ट्रीम का उपयोग करने के बारे में कैसे?

lazy var userIsLoggedInPublisher: AnyPublisher = $currentUser
                                          .map{$0 != nil}
                                          .eraseToAnyPublisher()

इस तरह, सदस्यता नदी के ऊपर से तत्व मिल जाएगा, तो आप उपयोग कर सकते हैं sinkया assignऐसा करने के लिए didSetविचार।


2

जिस प्रॉपर्टी को आप ट्रैक करना चाहते हैं, उसकी सदस्यता के लिए एक नया प्रकाशक बनाएं।

@Published var speed: Double = 88

lazy var canTimeTravel: AnyPublisher<Bool,Never> = {
    $speed
        .map({ $0 >= 88 })
        .eraseToAnyPublisher()
}()

फिर आप इसे अपनी @Publishedसंपत्ति की तरह देख पाएंगे ।

private var subscriptions = Set<AnyCancellable>()


override func viewDidLoad() {
    super.viewDidLoad()

    sourceOfTruthObject.$canTimeTravel.sink { [weak self] (canTimeTravel) in
        // Do something…
    })
    .store(in: &subscriptions)
}

प्रत्यक्ष रूप से संबंधित नहीं है, लेकिन फिर भी, आप इस तरह से कई गुणों को ट्रैक कर सकते हैं combineLatest

@Published var threshold: Int = 60

@Published var heartData = [Int]()

/** This publisher "observes" both `threshold` and `heartData`
 and derives a value from them.
 It should be updated whenever one of those values changes. */
lazy var status: AnyPublisher<Status,Never> = {
    $threshold
       .combineLatest($heartData)
       .map({ threshold, heartData in
           // Computing a "status" with the two values
           Status.status(heartData: heartData, threshold: threshold)
       })
       .receive(on: DispatchQueue.main)
       .eraseToAnyPublisher()
}()

0

आप अपने ऑब्जर्वेबल ओब्जेक्ट में एक पाश्चर्यूस्यूबजेक्ट को घोषित करते हैं:

class ReactiveUserManager1: ObservableObject {

    //The PassthroughSubject provides a convenient way to adapt existing imperative code to the Combine model.
    var objectWillChange = PassthroughSubject<Void,Never>()

    [...]
}

और अपने @Published var के डिस्सेट (willSet बेहतर हो सकता है) में आप एक विधि का उपयोग करेंगे जिसे भेजें () कहा जाता है।

class ReactiveUserManager1: ObservableObject {

    //The PassthroughSubject provides a convenient way to adapt existing imperative code to the Combine model.
    var objectWillChange = PassthroughSubject<Void,Never>()

    @Published private(set) var currentUser: User? {
    willSet {
        userIsLoggedIn = currentUser != nil
        objectWillChange.send()
    }

    [...]
}

आप इसे WWDC डेटा फ्लो टॉक में देख सकते हैं


आपको कंबाइन
निकोला लॉरिटानो

यह प्रश्न 1 में विकल्प 1 से कैसे अलग है?
nayem

वहाँ कोई विकल्प 1 में PassthroughSubject नहीं है
निकोला लॉरिटानो

खैर, यह वह नहीं था जो मैंने वास्तव में पूछा था। @Publishedआवरण और PassthroughSubjectदोनों इस संदर्भ में एक ही उद्देश्य से काम करते हैं। ध्यान दें कि आपने क्या लिखा था और ओपी वास्तव में क्या हासिल करना चाहता था। क्या आपका समाधान बेहतर विकल्प के रूप में काम करता है जो विकल्प 1 वास्तव में है?
9

0

स्कैन ( : :) क्लोजर द्वारा वापस लाए गए अंतिम मान के साथ एक बंद करने के लिए वर्तमान तत्व प्रदान करके अपस्ट्रीम प्रकाशक से तत्वों को ट्रांसफ़ॉर्म करता है।

आप नवीनतम और वर्तमान मूल्य प्राप्त करने के लिए स्कैन () का उपयोग कर सकते हैं। उदाहरण:

@Published var loading: Bool = false

init() {
// subscriber connection

 $loading
        .scan(false) { latest, current in
                if latest == false, current == true {
                    NotificationCenter.default.post(name: NSNotification.Name("userStateDidChange"), object: nil) 
        }
                return current
        }
         .sink(receiveValue: { _ in })
         .store(in: &subscriptions)

}

उपरोक्त कोड इसके बराबर है: (कम गठबंधन)

  @Published var loading: Bool = false {
            didSet {
                if oldValue == false, loading == true {
                    NotificationCenter.default.post(name: NSNotification.Name("userStateDidChange"), object: nil)
                }
            }
        }
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.