8

I have the following struct of properties for Chat

struct Chat {
    var id = String()
    var gender = String()
    var date = Date()

    init() {}
}

In a view controller, i declare an instance of Chat called observablechat, and then i used the flatmap operator to attempt and observe only changes in the date property of observablechat. However, if i change the gender property (as shown), the subscription gets triggered. I wonder why that is and how can i fix this code such that the subscription only looks at what happens to the date property and nothing else?

class ViewController: UIViewController {

    var observablechat = Variable(Chat())

        observablechat.asObservable()
        .flatMap { (Chat) -> Observable<Date> in
            return Observable.of(Chat.matchDate)
        }.subscribe(onNext: { (r) in
            print(r)
        }).addDisposableTo(disposeBag)


        self.observablechat.value.gender = "male" 
        //triggers subscription above. 
    }
}
Alexander
  • 59,041
  • 12
  • 98
  • 151
Ryan
  • 969
  • 1
  • 6
  • 19

1 Answers1

9

First of all, why flatMap?

You need only distinctUntilChanged and map. DebugPrint will be triggered only twice: initial setup + date changed once. Check out the code below and feel free to ask questions.

import PlaygroundSupport
import RxSwift

struct Chat {

    var id: String
    var gender: String
    var date: Date

    init(id: String = "", gender: String = "", date: Date = Date(timeIntervalSince1970: 0)) {
        self.id = id
        self.gender = gender
        self.date = date
    }
}

final class YourClass {

    lazy var variableChat: Variable<Chat> = Variable<Chat>(Chat())

    var observableDate: Observable<Date> {
        return variableChat
            .asObservable()
            .distinctUntilChanged({ (chatOld, chatNew) -> Bool in
                return chatOld.date == chatNew.date
            })
            .map({ (chat) -> Date in
                return chat.date
            })
            .shareReplay(1)
    }
}

let value = YourClass()
value.observableDate
    .subscribe(onNext: { (date) in
        debugPrint(date.timeIntervalSince1970)
    })
value.variableChat.value.gender = "male"
value.variableChat.value.date = Date()
value.variableChat.value.gender = "female"
value.variableChat.value.gender = "male"
value.variableChat.value.gender = "female"

P.S. the way to run RxSwift in playground: readme

iWheelBuy
  • 5,470
  • 2
  • 37
  • 71
  • Distinctuntilchanged() , thats what i was missing!! And honestly I'm not too sure why i used flatmap. I guess map seems more practiical .. Also, why did you use shareReplay(1)? I get replay is to keep a buffer size, but why share? (Sorry if thats a dumb question, I'm sort of a beginner with rx) – Ryan Apr 29 '17 at 15:35
  • 1
    Lets imagine you have some observable which consists of map. And you make multiple subcriptions. With shareReplay map of original observable will be performed only once. Without shareReplay map of original observable will be called for each subscription... I think you need to check the way it works in playground: http://paste.org.ru/?yndoa0 But be carefull and don't use it until you fully understand it (: – iWheelBuy Apr 29 '17 at 16:01
  • 2
    Will do! i noticed adding rx code that you're unfamiliar with is like playing with fire. Time to read the docs. Thank you for your time and solution man! – Ryan Apr 29 '17 at 16:24
  • 1
    This is a good answer. A couple of comments... You don't have to call `timeIntervaleSince1970` on the date objects because in Swift 3 Dates are Equatable, and if you put the `map` to dates before the `distinctUntilChanged`, you don't need to provide a closure. Also, you only need `shareReplay` for cold observables. `Variable` is already hot, i.e., it already has shareReplay capability. Adding shareReplay here won't hurt anything, but it doesn't change the behavior at all. – Daniel T. Apr 29 '17 at 18:45