Ios Swift Combine:`append`不要求输出相等?

Ios Swift Combine:`append`不要求输出相等?,ios,swift,combine,Ios,Swift,Combine,使用苹果的联合收割机,我想在第一个发布者foo完成后添加一个发布者bar(确定将Failure约束为Never)。基本上我想要 我有这样的想法: 让foo:AnyPublisher=/*实际发布者无关*/ let bar:AnyPublisher=/*实际发布者不相关*/ //A希望连接'bar'以开始生成元素 //只有在“foo”完成之后,比方说我只关心 //“foo”的第一个元素。 让fooThenBar=foo.first() .ignoreOutput() .append(bar)//编

使用苹果的联合收割机,我想在第一个发布者
foo
完成后添加一个发布者
bar
(确定将
Failure
约束为
Never
)。基本上我想要

我有这样的想法:

让foo:AnyPublisher=/*实际发布者无关*/
let bar:AnyPublisher=/*实际发布者不相关*/
//A希望连接'bar'以开始生成元素
//只有在“foo”完成之后,比方说我只关心
//“foo”的第一个元素。
让fooThenBar=foo.first()
.ignoreOutput()
.append(bar)//编译错误:`无法将类型为'AnyPublisher'的值转换为预期的参数类型'publisher.IgnoreOutput.Output'(也称为'Never')`
我想出了一个解决方案,我认为它是可行的,但它看起来非常丑陋/过于复杂

让fooThenBar=foo.first()
.ignoreOutput()
.flatMap{inempty()}
.附加(条)
我想我错过了什么

编辑
在下面的答案中添加了我最初提议的更好版本。非常感谢@RobNapier

我认为,您只需要过滤所有项目,然后附加以下内容,而不是
ignoreOutput

let fooThenBar = foo.first()
    .filter { _ in false }
    .append(bar)
您可能会发现重命名
dropAll()
会更好:

扩展发布程序{
func dropAll()->publisher.Filter{Filter{infalse}
}
让fooThenBar=foo.first()
.dropAll()
.附加(条)

根本的问题是,
ignoreAll()
生成输出为Never的发布服务器,这通常是有意义的。但是在这种情况下,您只想在不更改类型的情况下使用值,这就是过滤。

由于与@RobNapier进行了大量讨论,我们得出了这样的结论:当两个发布者的输出不同时,
flatMap{Empty}.append(otherPublisher)
解决方案是最好的。因为我想在第一个/base/'foo'发布服务器完成后使用它,所以我在
publisher.IgnoreOutput
上编写了一个扩展,结果如下:

解决方案
protocolbaseforand然后{}
extension Publishers.IgnoreOutput:BaseForAndThen{}
扩展Combine.Future:baseforand然后{}
扩展发布服务器,其中Self:baseforand,Self.Failure==Never{
func和Then(thenPublisher:Then)->AnyPublisher,其中Then:Publisher,Then.Failure==Failure{
返回
flatMap{inEmpty(completeImmediately:true)}//与`init()相同`
.append(然后是publisher)
.删除任何发布者()
}
}
用法 在我的用例中,我希望控制/洞察基本发布服务器何时完成,因此我的解决方案基于此

ignoreOutput
由于第二个发布者(在下面的例子中)在第一个发布者完成之前不会开始生成元素(输出值),因此我使用操作符(还有一个操作符)在一次输出后使
bananaSubject
完成

bananaSubject.first().ignoreOutput().然后(appleSubject)
与未来一起 一个
Future
已经只生成一个元素,然后完成

futurebananer.然后(applePublisher)
试验 下面是完整的单元测试()

导入XCTest
进口联合收割机
礼宾果{
var价格:Int{get}
}

typealias只要您使用
.ignoreOutput()
,就可以安全地将“丑陋的”
.flatMap{in Empty()}
替换为简单的
.map{Fruit?.none!}
,它无论如何都不会被调用,只会更改输出类型。

是的,更简洁,更合理!与RxSwift类似的东西会很好,可能是完全的,而且是单一的特征。联合收割机的
未来
有点像一个单一的,但没有实用的flatMap操作符从出版商进入未来。没有
和(上面的代码基本上是什么)以及其他与这些“特征”(如RxSwift所称)相关的操作符。该死的,哈哈,我在简化我的场景时犯了一个错误。。。当我问这个问题时,我说
foo
bar
具有相同的
输出
,实际上这不是我的情况。因此,给定两种不同的输出,有没有比我的
Empty()
'hack'更优雅的解决方案?
.flatMap(Empty).append(bar)
应该与
相同。flatMap(bar)
ignoreOutput().flatMap{inbar}不会产生与
ignoreOutput()相同的结果。flatMap{Empty().append(bar)
,如果你玩弄我在上面问题更新中发布的示例代码,你会看到。或者我完全搞糊涂了!:你能确认你的代码确实有效吗?我一直在玩它,我从来没有看到任何东西从
fooThenBar
发出。您是否有一个示例(理想情况下使用PassthroughSubject或CurrentSubject之类的工具)来证明您的代码是有效的?@RobNapier我用一个完全独立的单元测试更新了这个问题,证明了您的答案是有效的,我的解决方案也是丑陋的。这是一个写得非常好的测试用例,但这也有点让人困惑。你不想让最终的出版商出版苹果而不是水果吗?(例如P.Output!=Q.Output。)是的,我的
为空的解决方案允许不同的输出,当输出相同时,您的解决方案更整洁。感谢所有反馈并帮助我得出重要结论。我发布了自己的第一个版本,作为扩展。
extension Publisher {
    func dropAll() -> Publishers.Filter<Self> { filter { _ in false } }
}

let fooThenBar = foo.first()
    .dropAll()
    .append(bar)