Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
3.9k views
in Technique[技术] by (71.8m points)

swift - How to zip two publishers if the second one depends on the values received by the first one?

The data that I need to create a model are split and only receivable via two API requests.

The first (query.snapshotPublisher()) publisher will get the main part of the model (inclusive it's ID) and the second request (getPersonalLikeCount(blogModel: blogModel, userID: "408VYXScmxUtUQB91EoTf0LEMgj2")) will get some additional data and needs the ID (which is part of the model that's why I'm passing the complete model) that I got from the first request.

If both requests would be independent I would use the .Zip(other: ) operator but since the second one depends on the first one I tried the following to be able to pass the model to the second publisher.

But with this approach the second publisher get's cancelled after sending the first value and therefore won't update the model even when the getPersonalLikeCount publisher gets new data and should publish them again.

private func getBlogs(from query: Query) -> AnyPublisher<BlogModel, Error> {
    let publisher = query.snapshotPublisher()
        .flatMap { [self] blogModel -> AnyPublisher<(BlogModel, Int), Never> in
            let blogModelPublisher = Just(blogModel)
            let likeCountPublisher = getPersonalLikeCount(blogModel: blogModel, userID: "408VYXScmxUtUQB91EoTf0LEMgj2")
            let combined = Combine.Publishers.Zip(blogModelPublisher, likeCountPublisher)
            return combined.eraseToAnyPublisher()
        }
        .map { blogModel, personalLikeCount -> BlogModel in
            var newModel = blogModel
            newModel.personalLikeCount = personalLikeCount
            return newModel
        }
        .eraseToAnyPublisher()
    return publisher
}

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

Semantically if you want to restart one request with a new one you use map followed by switchToLatest. If and only if you want to do things in parallel then you use flatMap. Oddly enough Combine does not have a concatMap which is what you would use to do things serially in other reactive frameworks. In your case its

  1. Make the first request
  2. Map into the second request
  3. Map both values into a tuple
  4. SwitchToLatest
  5. Map the tuple into your result
doTheFirstThingPublisher
 .map { firstValue in 
   doTheSecondThingPubisher(with: firstValue)
     .map { secondValue in (firstValue, secondValue) }
 }
 .switchToLatest()
 .map { firstValue, secondValue in
   makeFinalValue(from: firstValue, and: secondValue)
  }

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...