Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Issues with Swift's Combine framework CombineLatest

I went through the WWDC video of "Introducing Combine" where it was said that whenever a publisher value gets updated the CombineLatest gets called and updated. But the snippet I created works oddly.

class Mango {
    var enableButton = false
    @Published var userName = "admin"
    @Published var password = "poweruser"
    @Published var passwordAgain = "poweruser"
    var validatePassword: AnyCancellable {
        Publishers.CombineLatest($password, $passwordAgain).map { (password, reenterpass) -> String? in
            print("Is Password Same to \(password)? :", password == reenterpass)
            guard password == reenterpass else { return nil }
            return password
        }.eraseToAnyPublisher()
            .map { (str) -> Bool in
            print("In Map", str != nil)
            guard str != nil else { return false }
            return true
        }.assign(to: \.enableButton, on: self)
    }

    init() {
        validatePassword
    }

    func checkSub() {
        print("1. Is password same? ->",enableButton)
        password = "nopoweruser"
        print("2. Is password same? ->",enableButton)
    }
}

When I initialize and call the function checkSub() where the publisher 'password' is updated the CombineLatest does not get called. Why is it behaving oddly?

Input:

let mango = Mango()<br>
mango.checkSub()

Output:

Is Password Same to poweruser? : true  
In Map true  
1. Is password same? -> true  
2. Is password same? -> true
like image 633
Allen Savio Avatar asked Aug 01 '19 10:08

Allen Savio


People also ask

Can we use combine in Swift?

The Combine framework provides a declarative Swift API for processing values over time. These values can represent many kinds of asynchronous events. Combine declares publishers to expose values that can change over time, and subscribers to receive those values from the publishers.

What is CombineLatest Swift?

CombineLatest publisher collects the first value from all three publishers and emits them as a single tuple. CombineLatest continues sending new values even when only one publisher emits a new value. On the other hand, the Zip operator sends a new value only when all the publishers emit new values.

Is combine a framework?

Combine is a unified declarative framework for processing values over time. Learn how it can simplify asynchronous code like networking, key value observing, notifications and callbacks.


1 Answers

It seems like the issue is with memory management. The validatePassword cancellable is autoreleased, meaning that the subscription is completed as soon as you create it, since you do not retain it. Make it a property instead of computed property, using lazy var and it should work fine.

lazy var validatePassword: AnyCancellable = {
    Publishers.CombineLatest($password, $passwordAgain).map { (password, reenterpass) -> String? in
        print("Is Password Same to \(password)? :", password == reenterpass)
        guard password == reenterpass else { return nil }
        return password
    }.eraseToAnyPublisher()
        .map { (str) -> Bool in
        print("In Map", str != nil)
        guard str != nil else { return false }
        return true
    }.assign(to: \.enableButton, on: self)
}()

With lazy you are retaining the cancellable which gets released only after the object is released. So, this should work properly.

like image 63
Sandeep Avatar answered Oct 20 '22 19:10

Sandeep