美文网首页selector
如何快速理解 swift Combine

如何快速理解 swift Combine

作者: 大成小栈 | 来源:发表于2023-11-20 14:21 被阅读0次

    Combine 是 Swift 中的一个框架,用于处理和组合异步和事件驱动的操作。
    要快速理解 Combine,可以从以下几个关键概念入手:

    Publisher: 发布者是一个对象,它可以发出一系列的事件,比如值、错误或完成信号;
    Subscriber: 订阅者是一个对象,它可以订阅一个或多个发布者,并对发布者发出的事件做出响应;

    Operators(操作符):Combine 提供了许多操作符,可以用于转换、组合和处理发布者发出的事件。例如,map 用于映射值,filter 用于过滤事件,merge 用于合并多个发布者的事件等。

    Cancellable(可取消):订阅一个发布者会返回一个 Cancellable 对象,你可以使用这个对象来取消订阅,释放资源;

    Subject(主题):主题是 Combine 中的一种特殊类型的发布者,既可以发送值也可以接收值。有 PassthroughSubject 和 CurrentValueSubject 等类型;

    Schedulers(调度器):Combine 使用调度器来确定在哪个线程上执行操作。有一些内置的调度器,比如 DispatchQueue.main 和 DispatchQueue.global();

    下面是一个简单的例子,演示了 Combine 中的一些概念:

    import Combine
    
    // 创建一个发布者,发出一系列整数
    let publisher = [1, 2, 3, 4, 5].publisher
    
    // 订阅者,处理发布者发出的事件
    let subscriber = Subscribers.Sink<Int, Never>(receiveValue: { value in
        print("Received value: \(value)")
    })
    
    // 订阅发布者
    let cancellable = publisher.sink(receiveValue: { value in
        print("Received value: \(value)")
    })
    
    // 取消订阅
    cancellable.cancel()
    

    一个复杂一点的例子,演示了一个 viewModel 中combine的使用:

    enum ContentMode: Equatable {
            case `default`
            case text(text: String)
            case voice(path: String)
        }
        
        class ViewModel {
            /// 当前选中图片index
            @Published
            var currentPhotoIndex: Int = 0
            
            /// 文字脚本内容
            @Published
            var textScript: String = ""
            var textLanguage: String = ""
            /// 文字脚本 对应的语音音色
            @Published // TODO 类型修改为 详情中对应Model
            var speechVoice: SpeechVoiceView.Voice?
            
            /// 语音脚本内容
            @Published
            var audioScript: String = ""
            
            /// 输入内容样式Mode
            @Published
            var contentMode: ContentMode = .default
            
            /// 是否能够生成
            @Published
            var enableGenerate: Bool = false
            
            /// 详情数据
            @Published
            var detail: DetailModel?
            
            /// 用户图
            @Published
            var userPhotos: [PTStylePhoto] = []
            
            /// Demo图
            @Published
            var demoPhotos: [PTStylePhoto] = []
            
            /// 用户图+Demo图
            @Published
            var allPhotos: [PTStylePhoto] = []
            
            // 是否结束播放
            @Published
            var isEndVideoPlay: Bool = false
            
            // 是否结束播放
            @Published
            var isEndAudioPlay: Bool = false
            
            let itemWidth = ScreenWidth - 16.0 * 4
            
            var contentModeName: String {
                switch contentMode {
                case .default:
                    return ""
                case .text(_):
                    return "text"
                case .voice(_):
                    return "voice"
                }
            }
            
            
            var cancellables = Set<AnyCancellable>()
            
            init() {
                initialize()
            }
            
            private func initialize() {
                $demoPhotos
                    .sink { [weak self] in
                        guard let self else { return }
                        allPhotos = userPhotos + $0
                    }.store(in: &cancellables)
                
                // 查询用户图
                PTUserPhotoManager.manager.$allUserPhotos
                    .map({ $0.map { PTStylePhoto(userPhoto: $0) } })
                    .sink { [weak self] value in
                        guard let self else { return }
                        userPhotos = value
                    }
                    .store(in: &cancellables)
                //
                PTUserPhotoManager.manager.queryUserPhoto()
                
                // 请求详情数据
                PhotoTalkAPI.shared.publisher(.detail, type: DetailModel.self)
                    .loading("loading...")
                    .map { $0.result }
                    .retry(2)
                    .sink(receiveCompletion: { error in
                        
                    }, receiveValue: { [weak self] value in
                        guard let self else { return }
                        detail = value
                        if let demoList = value?.demoList {
                            demoPhotos = demoList.map({
                                PTStylePhoto(demo: $0)
                            })
                        }
                    })
                    .store(in: &cancellables)
                
                $userPhotos.combineLatest($demoPhotos)
                    .dropFirst()
                    .map { $0 + $1 }
                    .assign(to: &$allPhotos)
                
    
                // 输入内容状态
                let textPublisher = $textScript.filter({
                    !$0.isEmpty
                }).map {
                    ContentMode.text(text: $0)
                }.removeDuplicates()
                
                let audioPublisher = $audioScript.filter({
                    !$0.isEmpty
                }).map {
                    ContentMode.voice(path: $0)
                }.removeDuplicates()
                
                textPublisher
                    .merge(with: audioPublisher)
                    .assign(to: &$contentMode)
                
                
                // 能否合成状态
                $contentMode.sink { [unowned self] x in
                    switch x {
                    case .default:
                        enableGenerate = false
                    case let .text(text):
                        enableGenerate = !text.isEmpty
                    case let .voice(path):
                        enableGenerate = !path.isEmpty
                    }
                }.store(in: &cancellables)
                
            }
    
        }
    

    这只是 Combine 的入门,你可以通过深入学习每个概念、查阅文档以及进行实际的应用来更全面地理解 Combine。 Combine 的强大之处在于它提供了一种声明式的方式来处理异步操作,使得代码更易于理解和维护。

    想详细了解Combine,请转以下链接:
    https://icodesign.me/posts/swift-combine/

    Combine中的操作符,逐个举例说明

    Combine 框架中提供了许多强大的操作符,用于处理和组合异步事件流。下面是一些常用的 Combine 操作符,逐个举例说明它们的用法:

    map 操作符:用于映射每个元素。

    let publisher = [1, 2, 3].publisher
    _ = publisher.map { $0 * 2 }
              .sink { value in
                  print(value) // 输出:2, 4, 6
              }
    

    filter 操作符:用于过滤元素。

    let publisher = [1, 2, 3, 4, 5].publisher
    _ = publisher.filter { $0 % 2 == 0 }
              .sink { value in
                  print(value) // 输出:2, 4
              }
    

    combineLatest 操作符:用于合并多个发布者的最新元素。

    let publisher1 = PassthroughSubject<Int, Never>()
    let publisher2 = PassthroughSubject<String, Never>()
    
    _ = publisher1.combineLatest(publisher2)
               .sink { value in
                   print(value)
               }
    
    publisher1.send(1)
    publisher2.send("A")
    // 输出:(1, "A")
    

    merge 操作符:
    用于合并多个发布者的元素。

    let publisher1 = PassthroughSubject<Int, Never>()
    let publisher2 = PassthroughSubject<Int, Never>()
    
    _ = Publishers.Merge(publisher1, publisher2)
              .sink { value in
                  print(value)
              }
    
    publisher1.send(1)
    publisher2.send(2)
    // 输出:1, 2
    

    flatMap 操作符:
    用于将每个元素映射到一个新的发布者,然后将这些发布者的元素合并成一个新的发布者。

    let publisher = [1, 2, 3].publisher
    _ = publisher.flatMap { value in
                 Just(value * 2)
              }
              .sink { value in
                  print(value) // 输出:2, 4, 6
              }
    

    scan 操作符:
    用于对元素进行累积操作。

    let publisher = [1, 2, 3].publisher
    _ = publisher.scan(0) { accumulator, value in
                 accumulator + value
              }
              .sink { value in
                  print(value) // 输出:1, 3, 6
              }
    

    这只是一小部分 Combine 操作符,它们提供了强大的工具,用于处理和组合异步事件流。
    根据具体的业务需求,你可以组合使用这些操作符来构建复杂的异步数据处理流程。

    相关文章

      网友评论

        本文标题:如何快速理解 swift Combine

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