美文网首页SwiftUI
SwiftUI 与 Combine(二)

SwiftUI 与 Combine(二)

作者: DkJone | 来源:发表于2020-03-03 15:20 被阅读0次

    操作符

    本文将介绍Combine中的各类运算符,这些运算符不要求你能够立即掌握,只要记得有某种运算符具有某种作用,在实际编程中可以将本文作为文档查询(ps:作者在使用RXSwift时也会经常查询某些不常用的操作符)
    正如Swift标准库中对数组的操作符(map、filter、zip...)一样,publisher也有着变形、筛选、链接等等操作符

    collect

    本操作符将单个元素缓存到集合中(可以指定缓存个数,满足个数或者收到完成事件后),然后发送集合


    15785495862104.jpg
    var subscriptions = Set<AnyCancellable>()
    var subscriptions = Set<AnyCancellable>()
    example(of: "collect") {
        ["A", "B", "C", "D", "E"].publisher
    //        .collect()
    //        .collect(2)
            .sink(receiveCompletion: { print($0) },
                  receiveValue: { print($0) })
            .store(in: &subscriptions)
    }
    /* 输出
    ——— Example of: collect ———
    A
    B
    C
    D
    E
    finished
    
    // 打开.collect()注释
    ——— Example of: collect ———
    ["A", "B", "C", "D", "E"]
    finished
    
    // 注释掉.collect()打开.collect(2)注释
    ——— Example of: collect ———
    ["A", "B"]
    ["C", "D"]
    ["E"]
    finished
    */
    

    使用.collect和其他没有指定个数或缓存大小的操作符时,注意他们讲使用没有内存大小限制的缓存区存储收到的元素。注意内存溢出

    map(_:)

    和Swift标准库中集合的Map类似,使用函数、闭包、或者keyPath来转变值

    /// 将数字转换成对应的ASCll值的字符,无法转换时返回空字符
    func numToString(num: Int) -> String {
        .init(Character(Unicode.Scalar(num) ?? "\0"))
    }
    example(of: "map") {
        (65...67)
            .publisher
            .map(numToString(num:))
            .print()
            .map(\.localizedLowercase)
            .sink(receiveCompletion: { print($0) }, receiveValue: { print($0) })
            .store(in: &subscriptions)
    }
    /* 输出
    ——— Example of: map ———
    receive subscription: (["A", "B", "C", "D", "E"])
    request unlimited
    receive value: (A)
    a
    receive value: (B)
    b
    receive value: (C)
    c
    receive finished
    finished
    */
    

    print

    打印接受到的值或者完成事件,通常用在调试的时候

    tryMap

    包括map在内的几个操作符都有一个对应的try操作符,该操作符将接受可能引发错误的闭包。如果有错误抛出,它将发送错误完成事件

    example(of: "tryMap") {
        let publisher = PassthroughSubject<String, Never>()
    
        publisher
            .tryMap { try JSONSerialization.jsonObject(with: $0.data(using: .utf8)!, options: .allowFragments) }
            .sink(receiveCompletion: { print($0) },
                  receiveValue: { print($0) })
            .store(in: &subscriptions)
    
        publisher.send(#"{"name":"DKJone"}"#)
        publisher.send("not a JSON")
    }
    
    /* 输出
    ——— Example of: tryMap ———
    {
        name = DKJone;
    }
    failure(Error Domain=NSCocoaErrorDomain Code=3840 ...."})
    */
    

    此处使用的#""#为原始字符串更多内容查看Swift5新特性 & XCode 10.2更新

    flatMap

    在swift标准库中FlatMap用来使多为数组扁平化为一位数组,并且在swift4.1中被重命名为Swift 4.1中被重命名为compactMap,在Combine中flatmap的作用稍微有点不同。
    flatmap作用是在publisher内部仍然包含publisher时可以将事件扁平化,具体查看以下代码:

    example(of: "flatMap") {
        // 1 定义三个人用于聊天
        let 小明 = Chatter(name: "小明", message: "小明: 鄙人王小明!")
        let 老王 = Chatter(name: "老王", message: "老王: 我就是隔壁老王!")
        let 于大爷 = Chatter(name: "于大爷", message: "烫头去")
    
        // 2 j可以获取当前值的Publishers初始值是小明
        let chat = CurrentValueSubject<Chatter, Never>(小明)
    
    //    chat
    //        .flatMap(maxPublishers: .max(2)) { $0.message }
    //        .sink(receiveValue: { print($0) })
    //        .store(in: &subscriptions)
    
        chat.sink{ print($0.message.value)}
    
        小明.message.value = "小明: 马冬梅在家吗?"
        chat.value = 老王
        老王.message.value = "老王: 什么冬梅?"
        小明.message.value = "小明: 马冬梅啊!"
        chat.value = 于大爷
        老王.message.value = "老王: 马东什么?"
    }
    
    /* 输出
    ——— Example of: flatMap ———
    小明: 鄙人王小明!
    老王: 我就是隔壁老王!
    烫头去
    
    // 注释chat.sink{ print($0.message.value)}打开上放订阅的注释输出
    ——— Example of: flatMap ———
    小明: 鄙人王小明!
    小明: 马冬梅在家吗?
    老王: 我就是隔壁老王!
    老王: 什么冬梅?
    小明: 马冬梅啊!
    老王: 马东什么?
    */
    
    

    在一开始我们使用chat.sink{ print($0.message.value)}订阅事输出只有三句话,这是因为我们只对chat进行了订阅。当具体的Chatter的message变化时,我们并不能订阅到事件。那我想订阅全部的谈话事件怎么办呢?flatMap正是为此而生的,当我们改成flatmap的订阅后可以输出所有publisher的事件,包括publisher的值内部的publisher发出的事件。上面我们也提到,map操作会缓存publisher,为了防止我们缓存太多的publisher我们可以在flatmap事指定缓存的publisher个数(maxPublishers: .max(2)),所以于老师并没有被没缓存,也就没有发出于老师说话的事件。如果未指定个数,则maxPublishers默认为.unlimited

    15785615311395.jpg

    compactMap

    和swift标准库中的数组操作符一样,如果你在转换时不需要转换失败的nil值,你使用此方法将的到一个没有可选值的序列

    example(of: "compactMap") {
        let strings = ["a", "1.24", "3", "def", "45", "0.23"].publisher
    
        // 2
        strings
            .compactMap { Float($0) }
            .sink(receiveValue: { print($0)})
            .store(in: &subscriptions)
    }
    /* 输出
    ——— Example of: compactMap ———
    1.24
    3.0
    45.0
    0.23
    */
    

    replaceNil(with:)

    对的它的作用和方法名一样直白,就是在遇到可选值为空时用默认值替换空值,原始的序列也变成一个非空序列

    example(of: "replaceNil") {
      ["A", nil, "C"].publisher
        .replaceNil(with: "-")
        .map { $0! }
        .sink(receiveValue: { print($0) }) 
        .store(in: &subscriptions)
    }
    /* 输出
    ——— Example of: replaceNil ———
    A
    -
    C
    */
    

    replaceEmpty(with:)

    替换空序列

    example(of: "replaceEmpty(with:)") {
      let empty = Empty<Int, Never>()
      empty
        .replaceEmpty(with: 1)
        .sink(receiveCompletion: { print($0) },
              receiveValue: { print($0) })
        .store(in: &subscriptions)
    }
    

    Empty:创建一个只发出完成事件的序列

    scan(::)

    扫描之前的序列中所有元素,运用函数计算新值,接收一个初始值和每次接受到新元素的函数

    example(of: "scan") {
        // 1 一共进了10个球 每个球进球得分随机1~3分,每次进球后打印当前总分
        let score = (1..<10).compactMap { _ in (1...3).randomElement() }
        score
            .publisher
            .print()
            .scan(0, +)
            .sink(receiveValue: { print($0) })
            .store(in: &subscriptions)
    }
    /* 输出
    ——— Example of: scan ———
    receive subscription: ([1, 1, 2, 1, 1, 2, 3, 1, 3])
    request unlimited
    receive value: (1)
    1
    receive value: (1)
    2
    receive value: (2)
    4
    receive value: (1)
    5
    receive value: (1)
    6
    receive value: (2)
    8
    receive value: (3)
    11
    receive value: (1)
    12
    receive value: (3)
    15
    receive finished
    */
    

    注意.scan(0, +)这里的 + 是一个函数,你可以在Xcode点击查看其声明
    public static func + (lhs: Int, rhs: Int) -> Int

    tryScan

    作为一名有经验的程序员,你应该能自己联想到他的功能,对就是你想的那样。

    在后面的操作符中如果没有特殊功能将不再单独介绍相关的try运算符,但我会告诉你它的存在

    Filtering

    和标准库的Array的方法一样,该方法也是接受一个返回Bool值的函数过滤元素,返回false的值将被过滤掉

    example(of: "filter") {
        let numbers = [(1,2),(2,4),(3,1),(6,2),(1,0)].publisher
    
        // 过滤掉第一个值小于第二个值的元祖
        numbers
            .filter( > )
            .sink(receiveValue: { print("\($0) 大于 \($1)") })
            .store(in: &subscriptions)
    }
    /* 输出
    ——— Example of: filter ———
    3 大于 1
    6 大于 2
    1 大于 0
    */
    

    注意此处的 >是一个函数,这里的参数可以是函数(或者闭包-匿名函数)
    public static func > (lhs: Int, rhs: Int) -> Bool

    removeDuplicates

    过滤掉连续的重复值


    15828721202656.jpg
    example(of: "removeDuplicates") {
        // 1
        let userInput = ["aaa", "aaa", "bbbb", "ccc", "bbbb"].publisher
    
        // 2
        userInput
            .removeDuplicates()
            .sink(receiveValue: { print($0) })
            .store(in: &subscriptions)
    }
    /* 输出
    ——— Example of: removeDuplicates ———
    aaa
    bbbb
    ccc
    bbbb
    */
    

    ignoreOutput

    忽略发送的值,如果有的时候你只在乎publisher有没有完成发送二不关心他具体发送了那些值,可已使用ignoreOutput,使用后将只会订阅到完成事件(错误完成或者正常结束)

    example(of: "ignoreOutput") {
        // 1
        let numbers = (1...10_000).publisher
        // 2
        numbers
            .ignoreOutput()
            .sink(receiveCompletion: { print("Completed with: \($0)") },
                  receiveValue: { print($0) })
            .store(in: &subscriptions)
    }
    
    /* 输出
    ——— Example of: ignoreOutput ———
    Completed with: finished
    */
    

    compactMap

    转换时忽略掉无法转换的值,和标准库中的Array 的同名方法类似

    example(of: "compactMap") {
        let strings = ["http://www.baidu.com", "dkjone://happy.lv", "哈利路亚", "", "https://127.0.0.1"].publisher
    
        strings
            .compactMap(URL.init(string:))
            .sink(receiveValue: { print($0.absoluteString) })
            .store(in: &subscriptions)
    }
    
    /* 输出
    ——— Example of: compactMap ———
    http://www.baidu.com
    dkjone://happy.lv
    https://127.0.0.1
    */
    

    first(where:)

    找到序列中的第一个满足条件的值然后发出,并且发送完成事件,取消上游的publishers继续向其发送值

    example(of: "first(where:)") {
        let numbers = (1...9).publisher
        numbers
            .print("numbers")
            .first(where: { $0 % 2 == 0 })
            .sink(receiveCompletion: { print("Completed with: \($0)") },
                  receiveValue: { print($0) })
            .store(in: &subscriptions)
    }
    /* 输出
    ——— Example of: first(where:) ———
    numbers: receive subscription: (1...9)
    numbers: request unlimited
    numbers: receive value: (1)
    numbers: receive value: (2)
    numbers: receive cancel
    2
    Completed with: finished
    */
    

    last(where:)

    与first(where:)相反,此运算符是贪婪的,因为它必须等待所有值发出,才能知道是否找到匹配的值。因此,上游必须是一个已经经完成的publisher

    example(of: "last(where:)") {
        let numbers = PassthroughSubject<Int, Never>()
        numbers
            .last(where: { $0 % 2 == 0 })
            .sink(receiveCompletion: { print("Completed with: \($0)") },
                  receiveValue: { print($0) })
            .store(in: &subscriptions)
    
        numbers.send(1)
        numbers.send(2)
        numbers.send(3)
        numbers.send(4)
        numbers.send(5)
        numbers.send(completion: .finished)
    }
    /* 输出
    ——— Example of: last(where:) ———
    4
    Completed with: finished
    */
    

    dropFirst

    忽略指定个数的值

    example(of: "dropFirst") {
        let numbers = (1...10).publisher
        numbers
            .dropFirst(8)
            .sink(receiveValue: { print($0) })
            .store(in: &subscriptions)
    }
    /* 输出
    ——— Example of: dropFirst ———
    9
    10
    */
    

    drop(while:)

    忽略序列中的值直到满足条件时。

     example(of: "drop(while:)") {
        let numbers = (1...10).publisher
        numbers
            .drop(while: { $0 % 5 != 0 })
            .sink(receiveValue: { print($0) })
            .store(in: &subscriptions)
    }
    
    /* 输出
    ——— Example of: drop(while:) ———
    5
    6
    7
    */
    

    drop(untilOutputFrom:)

    忽略序列中对的值直到另一个序列开始发送值

     example(of: "drop(untilOutputFrom:)") {
        let isReady = PassthroughSubject<Void, Never>()
        let taps = PassthroughSubject<Int, Never>()
    
        taps
            .drop(untilOutputFrom: isReady)
            .sink(receiveValue: {
                print($0)
            })
            .store(in: &subscriptions)
    
        (1...5).forEach { n in
            taps.send(n)
            if n == 3 {
                isReady.send()
            }
        }
    }
    /* 输出
    ——— Example of: drop(untilOutputFrom:) ———
    4
    5
    */
    

    prefix

    获取序列中指定个数的值,然后发出完成事件

     example(of: "prefix") {
        let numbers = (1...10).publisher
        numbers
            .prefix(2)
            .sink(receiveCompletion: { print("Completed with: \($0)") },
                  receiveValue: { print($0) })
            .store(in: &subscriptions)
    }
    /* 输出
    ——— Example of: prefix ———
    1
    2
    Completed with: finished
    */
    

    prefix(while:)

    获取序列中的值直到满足给定的条件,然后发出完成事件,与 drop(while:)相反


    15828722470169.jpg

    prefix(untilOutputFrom:)

    获取序列中的值直到给定的序列发出值,然后发出完成事件,与 drop(untilOutputFrom:)相反


    15828722334301.jpg

    prepend

    在原始的publisher序列前面追加给定的值,值的类型必须与原始序列类型一致


    15828726074065.jpg
     example(of: "prepend(Output...)") {
      let publisher = [3, 4].publisher
      publisher
        .prepend(1, 2)
        .sink(receiveValue: { print($0) })
        .store(in: &subscriptions)
    }
    /* 输出
    ——— Example of: prepend(Output...) ———
    1
    2
    3
    4
    */
    

    prepend(Sequence)

    在原始的publisher序列前面追加给定序列中的所有值,值的类型必须与原始序列类型一致

    prepend(Publisher)

    在原始的publisher序列前面追加给定publisher 序列中的所有值,值的类型必须与原始序列类型一致,如果追加的publisher是一个未完成的序列,会等新追加序列发送完成事件再发送原始序列中的值

    example(of: "prepend(Publisher) #2") {
      let publisher1 = [3, 4].publisher
      let publisher2 = PassthroughSubject<Int, Never>()
    
      publisher1
        .prepend(publisher2)
        .sink(receiveValue: { print($0) })
        .store(in: &subscriptions)
    
      publisher2.send(1)
      publisher2.send(2)
    //  publisher2.send(completion: .finished)
    }
    /* 输出
    ——— Example of: prepend(Publisher) ———
    1
    2
    
    //打开最后一行注释后输出
    ——— Example of: prepend(Publisher) ———
    1
    2
    3
    4
    */
    

    append()

    与prepend() 类似,只是位置改为在原始序列末尾追加

    append(Sequence)

    与prepend(Sequence) 类似,只是位置改为在原始序列末尾追加

    15828732022683.jpg
     example(of: "append(Output...)") {
        let publisher = [1].publisher
    
        publisher
            .append(2, 3)
            .append(4)
            .sink(receiveValue: { print($0) })
            .store(in: &subscriptions)
    }
    /* 输出
    ——— Example of: append(Output...) ———
    1
    2
    3
    4
    */
    

    append(Publisher)

    与prepend(Publisher) 类似,只是位置改为在原始序列末尾追加

    switchToLatest

    从一个publiser切换到另一个,这会停止接收之前publisher的值而改成接收新切换的publisher序列中的值


    15828737758818.jpg
     example(of: "switchToLatest") {
        let publisher1 = PassthroughSubject<Int, Never>()
        let publisher2 = PassthroughSubject<Int, Never>()
        let publisher3 = PassthroughSubject<Int, Never>()
    
        let publishers = PassthroughSubject<PassthroughSubject<Int, Never>, Never>()
    
        publishers
            .switchToLatest()
            .sink(receiveCompletion: { _ in print("Completed!") },
                  receiveValue: { print($0) })
            .store(in: &subscriptions)
    
        publishers.send(publisher1)
        publisher1.send(1)
        publisher1.send(2)
    
        publishers.send(publisher2)
        publisher1.send(3)
        publisher2.send(4)
        publisher2.send(5)
    
        publishers.send(publisher3)
        publisher2.send(6)
        publisher3.send(7)
        publisher3.send(8)
        publisher3.send(9)
    
        publisher3.send(completion: .finished)
        publishers.send(completion: .finished)
    }
    /* 输出
    ——— Example of: switchToLatest ———
    1
    2
    4
    5
    7
    8
    9
    Completed!
    */
    

    merge(with:)

    与RXSwift中的同名操作符效果一致,将publishers中的值按时间顺序合并成一个publisher,值类型必须一致

    image
    example(of: "merge(with:)") {
        let publisher1 = PassthroughSubject<Int, Never>()
        let publisher2 = PassthroughSubject<Int, Never>()
    
        publisher1
            .merge(with: publisher2)
            .sink(receiveCompletion: { _ in print("Completed") },
                  receiveValue: { print($0) })
            .store(in: &subscriptions)
    
        publisher1.send(1)
        publisher1.send(2)
    
        publisher2.send(3)
    
        publisher1.send(4)
    
        publisher2.send(5)
    
        publisher1.send(completion: .finished)
        publisher2.send(completion: .finished)
    }
    
    /* 输出
    ——— Example of: merge(with:) ———
    1
    2
    3
    4
    5
    Completed
    */
    

    combineLatest

    与RXSwift中的同名操作符效果一致,当多个 publisher 中任何一个发出一个元素,就发出一个元素。这个元素是由这些 publisher 中最新的元素组合起来的元组,各个publisher的值类型可以不一样


    15828766695342.jpg
    example(of: "combineLatest") {
        let publisher1 = PassthroughSubject<Int, Never>()
        let publisher2 = PassthroughSubject<String, Never>()
    
        publisher1
            .combineLatest(publisher2)
            .sink(receiveCompletion: { _ in print("Completed") },
                  receiveValue: { print("P1: \($0), P2: \($1)") })
            .store(in: &subscriptions)
    
        publisher1.send(1)
        publisher1.send(2)
    
        publisher2.send("a")
        publisher2.send("b")
    
        publisher1.send(3)
    
        publisher2.send("c")
    
        publisher1.send(completion: .finished)
        publisher2.send(completion: .finished)
    }
    
    /* 输出
    ——— Example of: combineLatest ———
    P1: 2, P2: a
    P1: 2, P2: b
    P1: 3, P2: b
    P1: 3, P2: c
    Completed
    */
    

    zip

    与swift标准库和RXSwift的同名函数左营类似,在publisher相同索引处发出成对值的元组。它等待每个publisher发出一个值,然后在所有publisher在当前索引处发出一个元素后发出一个值的元组。这意味着,如果zip两个publisher,则每次两个发布服务器都发出一个值时,才会发出一个元组。


    image
    example(of: "zip") {
        let publisher1 = PassthroughSubject<Int, Never>()
        let publisher2 = PassthroughSubject<String, Never>()
    
        publisher1
            .zip(publisher2)
            .sink(receiveCompletion: { _ in print("Completed") },
                  receiveValue: { print("P1: \($0), P2: \($1)") })
            .store(in: &subscriptions)
    
        publisher1.send(1)
        publisher1.send(2)
    
        publisher2.send("a")
        publisher2.send("b")
    
        publisher1.send(3)
    
        publisher2.send("c")
        publisher2.send("d")
    
        publisher1.send(completion: .finished)
        publisher2.send(completion: .finished)
    }
    
    /* 输出
    ——— Example of: zip ———
    P1: 1, P2: a
    P1: 2, P2: b
    P1: 3, P2: c
    Completed
    */
    

    delay(for)

    延迟发出元素,接受一个延迟时间参数,以及要运行的线程

    var subscriptions = Set<AnyCancellable>()
    let start = Date()
    let deltaFormatter: NumberFormatter = {
        let f = NumberFormatter()
        f.negativePrefix = ""
        f.minimumFractionDigits = 1
        f.maximumFractionDigits = 1
        return f
    }()
    
    /// 本页代码运行时初始化时间, 计算运行到固定行数时得到时间差
    public var deltaTime: String {
        return deltaFormatter.string(for: Date().timeIntervalSince(start))!
    }
    
    example(of: "Delay") {
        let publisher = Timer.publish(every: 1, on: .main, in: .common).autoconnect()
    
        publisher.sink { date in
            print("origin:\t" + "\(deltaTime)\t" + date.description)
        }.store(in: &subscriptions)
    
        publisher.delay(for: .seconds(2), scheduler: DispatchQueue.main).sink { date in
            print("delay:\t" + "\(deltaTime)\t" + date.description)
        }.store(in: &subscriptions)
    }
    
    /* 输出
    ——— Example of: Delay ———
    origin: 1.1 2020-03-02 06:33:55 +0000
    origin: 2.1 2020-03-02 06:33:56 +0000
    origin: 3.1 2020-03-02 06:33:57 +0000
    delay:  3.3 2020-03-02 06:33:55 +0000
    origin: 4.1 2020-03-02 06:33:58 +0000
    delay:  4.1 2020-03-02 06:33:56 +0000
    ...
    */
    

    autoconnect() 在第一次订阅时立即连接。

    Collect

    将原始序列中的元素组成集合发出,可以接受的参数有集合个数、时间、个数或时间。满足参数条件就会发出集合,否则等待元素。(参数是时间和个数都设置时满足任一就会发出)

    example(of: "Collect") {
        let publisher = Timer.publish(every: 1, on: .main, in: .common).autoconnect()
    
        publisher.sink { date in
            print("origin:\t" + "\(deltaTime)\t" + date.description)
        }.store(in: &subscriptions)
    
        publisher.collect(.byTimeOrCount(DispatchQueue.main, .seconds(5), 3), options: .none) .sink { date in
            print("collect:\t" + "\(deltaTime)\t" + date.description)
        }.store(in: &subscriptions)
    }
    /* 输出
    ——— Example of: Collect ———
    origin: 1.1 2020-03-02 07:38:26 +0000
    origin: 2.1 2020-03-02 07:38:27 +0000
    origin: 3.1 2020-03-02 07:38:28 +0000
    collect:    3.1 [2020-03-02 07:38:26 +0000, 2020-03-02 07:38:27 +0000, 2020-03-02 07:38:28 +0000]
    origin: 4.1 2020-03-02 07:38:29 +0000
    origin: 5.1 2020-03-02 07:38:30 +0000
    collect:    5.1 [2020-03-02 07:38:29 +0000, 2020-03-02 07:38:30 +0000]
    ...
    */
    

    Debounce(for)

    与RXSwift同名操作符作用一致,等待多长时间后没有新值再发出该值,忽略中间连续变化的值


    debonce
    example(of: "Debounce") {
       let publisher = PassthroughSubject<String,Never>()
       let typingHelloWorld: [(TimeInterval, String)] = [
          (0.0, "H"),
          (0.1, "He"),
          (0.2, "Hel"),
          (0.3, "Hell"),
          (0.5, "Hello"),
          (0.6, "Hello "),
          (2.0, "Hello W"),
          (2.1, "Hello Wo"),
          (2.2, "Hello Wor"),
          (2.4, "Hello Worl"),
          (2.5, "Hello World")
        ]
        //模拟输入Hello Word
        typingHelloWorld.forEach { (delay,str) in
            DispatchQueue.main.asyncAfter(deadline: .now() + delay) {
                publisher.send(str)
            }
        }
    
        publisher.sink { data in
            print("origin:\t" + "\(deltaTime)\t" + data)
        }.store(in: &subscriptions)
    
        publisher.debounce(for: .seconds(1), scheduler: DispatchQueue.main).sink { data in
            print("debounce:\t" + "\(deltaTime)\t" + data)
        }.store(in: &subscriptions)
    }
    
    /* 输出
    ——— Example of: Debounce ———
    origin: 0.1 H
    origin: 0.2 He
    origin: 0.3 Hel
    origin: 0.4 Hell
    origin: 0.6 Hello
    origin: 0.7 Hello 
    debounce:   1.7 Hello 
    origin: 2.2 Hello W
    origin: 2.2 Hello Wo
    origin: 2.5 Hello Wor
    origin: 2.5 Hello Worl
    origin: 2.8 Hello World
    debounce:   3.8 Hello World
    */
    

    throttle

    与RXSwift同名操作符效果一致,在固定时间内只发出一个值,过滤掉其他值,你可以选择最新的值或者第一个值发出

    example(of: "throttle") {
       let publisher = PassthroughSubject<String,Never>()
    
        //模拟输入Hello Word
        typingHelloWorld.forEach { (delay,str) in
            DispatchQueue.main.asyncAfter(deadline: .now() + delay) {
                publisher.send(str)
            }
        }
    
        publisher.sink { data in
            print("origin:\t" + "\(deltaTime)\t" + data)
        }.store(in: &subscriptions)
    
        publisher.throttle(for: .seconds(1), scheduler: DispatchQueue.main, latest: false).sink { data in
            print("throttle:\t" + "\(deltaTime)\t" + data)
        }.store(in: &subscriptions)
    }
    /* 输出
    ——— Example of: throttle ———
    origin: 0.1 H
    throttle:   0.1 H
    origin: 0.2 He
    origin: 0.3 Hel
    origin: 0.4 Hell
    origin: 0.6 Hello
    origin: 0.7 Hello 
    throttle:   1.2 Hello 
    origin: 2.2 Hello W
    origin: 2.2 Hello Wo
    throttle:   2.2 Hello Wo
    origin: 2.5 Hello Wor
    origin: 2.5 Hello Worl
    origin: 2.8 Hello World
    throttle:   3.5 Hello World
    */
    

    Timeout

    与RXSwift同名操作符一样,超出给定的时间后发出结束事件,你可以给出自定义错误类型,这样在超时后会发出错误完成事件


    image

    Measuring time

    用于调试的操作符,计算两次值发出的时间间隔,单位是纳秒

    min

    发出原始序列中的最小值,然后发出完成事件。要求序列是已完成的序列否则会等待原始序列的完成事件。

    max

    与min相反,发出原始序列中的最大值,然后发出完成事件。要求序列是已完成的序列否则会等待原始序列的完成事件。

    first

    发出原始序列中的第一个值,然后发出完成事件

    last

    与first相反,发出原始序列中的最后一个值,然后发出完成事件。要求序列是已完成的序列否则会等待原始序列的完成事件。

    output(in:)

    output(at:)发出指定位置的值,output(in:)发出指定范围内的值

    example(of: "output(in:)") {
      let publisher = ["A", "B", "C", "D", "E"].publisher
      publisher
        .print("publisher")
        .output(in: 1...3)
        .sink(receiveCompletion: { print($0) },
              receiveValue: { print("Value in range: \($0)") })
        .store(in: &subscriptions)
    }
    /*输出
    ——— Example of: output(in:) ———
    publisher: receive subscription: (["A", "B", "C", "D", "E"])
    publisher: request unlimited
    publisher: receive value: (A)
    publisher: request max: (1) (synchronous)
    publisher: receive value: (B)
    Value in range: B
    publisher: receive value: (C)
    Value in range: C
    publisher: receive value: (D)
    Value in range: D
    publisher: receive cancel
    finished
    */
    

    count

    记录原始序列发出值的个数,并发出

    contains

    原始publisher发出指定值,则contains运算符将发出true并取消订阅;如果原始publisher已完成且发出的值均不等于指定值,则发出false

    reduce

    与标准库函数类似,在原始publisher完成时发出累计计算的到的值


    15832015826163.jpg

    相关文章

      网友评论

        本文标题:SwiftUI 与 Combine(二)

        本文链接:https://www.haomeiwen.com/subject/vgfhlhtx.html