flatMap can someone explain this behaviour while using a subject?

Each time I think I have an understanding about the flatMap operator I come to some issue that make me feel I don't understand nothing :confused:.
The example I'm going to post don't make any sense is just to show the issue and I'm not looking to achieve nothing in that way, just trying to understand why is this happening.
Take this snippet as example:

let cvsOne = CurrentValueSubject<String, Never>("Kitty")

let canc =  ["Small","Warm"].publisher
.handleEvents(receiveSubscription: { (sub) in
    print("Step 1 Received sub \(sub)")
}, receiveOutput: { (value) in
    print("Step 1 Received output \(value)")
}, receiveCompletion: { (completion) in
    print("Step 1 Received completion \(completion)")
}, receiveCancel: {
    print("Step 1 Received cancel")
}, receiveRequest: { (req) in
     print("Step 1 Received request \(req)")
})
.flatMap { (val)  in
    return cvsOne
}
.handleEvents(receiveSubscription: { (sub) in
    print("Step 2 Received sub \(sub)")
}, receiveOutput: { (value) in
    print("Step 2 Received output \(value)")
}, receiveCompletion: { (completion) in
    print("Step 2 Received completion \(completion)")
}, receiveCancel: {
    print("Step 2 Received cancel")
}, receiveRequest: { (req) in
    print("Step 2 Received request \(req)")
})
.sink { (value) in
    print("Sink Received \(value)")
}

Here is the output of the console, as I expected the sink is called 2 times.

Step 2 Received request unlimited
Step 2 Received sub FlatMap
Step 1 Received request unlimited
Step 1 Received sub ["Small", "Warm"]
Step 1 Received output Small
Step 2 Received output Kitty
Sink Received Kitty
Step 1 Received output Warm
Step 2 Received output Kitty
Sink Received Kitty
Step 1 Received completion finished

Taking the same code this is what happen if, after the first execution, I send

cvsTwo.send("Little ball")

The sink is executed 2 more times, I would have expect just one.

Sending more data

Step 2 Received output Little ball
Sink Received Little ball
Step 2 Received output Little ball
Sink Received Little ball

I really can't figure out why is this happening, it seems that the subscriber is subscribed the same number of times as the count of the array published. Of course if I increment the number of elements in the array it will grow accordingly.
Is there someone that could explain me this behaviour?

I assume you mean cvsOne.send here? Otherwise your code doesn't compile.

This is how I understand flatMap:

  • The publisher that flatMap returns subscribes to all publishers that you return from your flatMap closure.

  • Your flatMap closure returns the same subject twice, so flatMap subscribes twice to the subject. So the subject now has two subscribers – it doesn't know that both subscriptions belong to the same owner.

  • The subject sends every value it receives to all of its subscribers. So every value you send to the subject is sent twice to the flatMap publisher.

By the way, you can replace the long .handleEvents calls with .print. Very useful for debugging. Like this:

let canc =  ["Small", "Warm"].publisher
  .print("Step 1")
  .flatMap { val  in
    return cvsOne
  }
  .print("Step 2")
  .sink { value in
    print("Sink Received \(value)")
  }
1 Like

Ole, thank you so much for you answer.
Yes, I was meaning cvsOne but I made so many attempts trying to understand that probably I pasted a wrong piece of sample.
I think that you clarify an important concept that wasn't clear to me that, in fact, my flatMap returns the same subject twice and that now has two subscribers.

1 Like
Terms of Service

Privacy Policy

Cookie Policy