Combine有很多方便的方法: Operators。这些operators可以把从publisher传出来的values一层一层的过滤/筛选/变型,subscriber就可以拿到自己真正需要的values了。
1、collect()
collect就是把一个一个输出的value全都放在array里,然后等publisher结束后,输出这个array:
["A", "B", "C", "D", "E"].publisher
.collect()
.sink(receiveCompletion: { print($0) },
receiveValue: { print($0) })
.store(in: &subscriptions)
// ["A", "B", "C", "D", "E"]
// finished
通常我们会限制collect需要收集values的个数,不然无限等着可能浪费内存:
.collect(2)
// ["A", "B"]
// ["C", "D"]
// ["E"]
// finished
2、map()
这和swift中的用法一样
["A", "B", "C"].publisher
.map { $0 + $0 }
.sink(receiveCompletion: { print($0) },
receiveValue: { print($0) })
.store(in: &subscriptions)
AA
BB
CC
finished
3、map key path
如果输出的value有多个属性,那么也可以用map和keypath来拆分:
[(x: 2, y: 3), (x: 1, y: 5), (x: 2, y: 6)].publisher
.map(\.x, \.y)
.sink(receiveCompletion: { print($0) },
receiveValue: { x, y in print(x + y) })
.store(in: &subscriptions)
5
6
8
finished
4、tryMap
tryMap就是map的closure里有可能throw,throw的话就筛选掉这个value:
Just("/fake")
.tryMap { try FileManager.default.contentsOfDirectory(atPath: $0) }
.sink(receiveCompletion: { print($0) },
receiveValue: { print($0) })
.store(in: &subscriptions)
No such file or directory
5、flatMap
如果你有一个publisher发送的value的某些属性也是publisher, 那sink只会receive外面这个publisher发出的value,属性publisher发出的value要是也想接收,那你就可以用flatMap
struct Person {
let name : String
var age: CurrentValueSubject
init(name: String, age: Int) {
self.name = name
self.age = CurrentValueSubject(age)
}
}
var me = Person(name: "Joshua", age: 18)
var you = Person(name: "Dog", age: 28)
let people = PassthroughSubject()
people
.flatMap{ $0.age }
.sink(receiveCompletion: { _ in
print("people completed")
}, receiveValue: { val in
print(val)
})
people.send(me)
people.send(you)
me.age.value = 19
you.age.value = 30
//输出
18
28
19
30
好好看一下这个例子,我觉得大家应该就都理解了吧,这属于比较复杂的一个operator了。flatMap还可以限制订阅publisher的个数:
var me = Person(name: "Joshua", age: 18)
var you = Person(name: "Dog", age: 28)
var she = Person(name: "SHE", age: 16)
let people = PassthroughSubject()
people
.flatMap(maxPublishers: .max(2)) { $0.age }
.sink(receiveCompletion: { _ in
print("people completed")
}, receiveValue: { val in
print(val)
})
people.send(me)
people.send(you)
people.send(she)
me.age.value = 19
you.age.value = 30
she.age.value = 17
// 输出和之前那个一样
6、replaceNil(with:)
"人"如其名的这种我就不解释了
["A", nil, "C"].publisher
.replaceNil(with: "null")
.map { $0! }
.sink(receiveCompletion: { print($0) },
receiveValue: { print($0) })
.store(in: &subscriptions)
A
null
C
finished
7、replaceEmpty(with:)
let empty = Empty()
empty
.replaceEmpty(with: "Empty")
.sink(receiveCompletion: { print($0) },
receiveValue: { print($0) })
.store(in: &subscriptions)
Empty
finished
这个Empty一般就是用来测试或demo,直接发送.finish
8、reduce(0)
这个和之前文章中介绍的reduce一样。publisher一定要结束才会输出最后值
[1, 2, 3].publisher
.reduce(0, +)
.sink(receiveCompletion: { print($0) },
receiveValue: { print($0) })
.store(in: &subscriptions)
6
finished
9、scan
scan和reduce差不多,但是scan不是最后才输出值,每个都会输出
[1, 2, 3].publisher
.scan(0, +)
.sink(receiveCompletion: { print($0) },
receiveValue: { print($0) })
.store(in: &subscriptions)
1
3
6
finished
10、filter
[1, 2, 3].publisher
.filter { $0 < 2 }
.sink(receiveCompletion: { print($0) },
receiveValue: { print($0) })
.store(in: &subscriptions)
1
finished
11、removeDuplicates()
注意这个并不是所有重复的都去掉,只是publisher当前要发出的value和上个发出的一样的才会去掉。要让系统排重,那value一定是Equatable, 不然你就得自定义相等的条件:
[1, 1, 2].publisher
.removeDuplicates()
.sink(receiveCompletion: { print($0) },
receiveValue: { print($0) })
.store(in: &subscriptions)
1
2
finished
12、compactMap
跟swift中的一样,会删掉invalid的值
["1", "a", "2"].publisher
.compactMap { $0 }
.sink(receiveCompletion: { print($0) },
receiveValue: { print($0) })
.store(in: &subscriptions)
1
2
finished
13、ignoreOutput()
只想知道publisher是不是结束了,不care他发出了什么value
14、first() 和 first(where:)
接收到第一个值就取消订阅,可以用where加条件
[1, 2, 3].publisher
.first { $0 > 1 }
.sink(receiveCompletion: { print($0) },
receiveValue: { print($0) })
.store(in: &subscriptions)
2
finished
15、last()和last(where:)
跟上一个一样,唯一区别是publisher必须结束才会输出,原因不用说了吧
16、dropFirst()
.dropFirst(3)就是不要前3个
17、drop(while:)
直到while的条件符合,都不要!但一旦符合条件了,后面的都要!
[1, 2, 3, 4, 5].publisher
.drop(while: { $0 < 3 })
.sink(receiveCompletion: { print($0) },
receiveValue: { print($0) })
.store(in: &subscriptions)
3
4
5
finished
18、drop(untilOutputFrom:)
这个其实也很简单理解,就是你有两个publisher,用这个operator的publisher必须要等另一个publisher输出才会输出,之前的都不要了
19、prefix()、prefix(while:)、prefix(untilOutputFrom:)
跟drop的三个正好相反,保留前几个values直到....唯一要注意的是,跟first一样,符合条件之后就cancel
20、prepend()
在publisher输出之前先输出prepend里面的同类型values
[1, 2, 3].publisher
.prepend(9, 8) // 也可以prepend([9, 8]),结果一样
.sink(receiveCompletion: { print($0) },
receiveValue: { print($0) })
.store(in: &subscriptions)
9
8
1
2
3
finished
还可以prepend(另一个publisher),这样只有prepend的publisher完成输出,本来的publisher才开始输出
21、append()
嗯没错,跟之前相反,但你要注意,只有publisher结束了,才会输出append的values
现写到这儿...还有一半...shit...