[Swift] 扩展与无缝连接 Combine/RxSwift 和 Concurrency 的运算符

   2023-02-07 学习力0
核心提示:* 本文将主要描述Combine的示例代码。* 在示例中,AnyCancellable 和 DisposeBag 没有描述,因为它们是多余的。注意。首先架构如Combine/RxSwift反应式编程如果您使用Model 或Repository,您可以使用响应式编程编写连接部分。 (因为拼接容易,代码变漂亮了)

* 本文将主要描述Combine的示例代码。
* 在示例中,AnyCancellableDisposeBag 没有描述,因为它们是多余的。注意。

首先

架构如Combine/RxSwift反应式编程如果您使用ModelRepository,您可以使用响应式编程编写连接部分。 (因为拼接容易,代码变漂亮了)

但随着Concurrency 的引入,这种情况即将改变。

这一次,当我用Concurrency替换了一些处理时,就需要从Combine调用Concurrency,所以我试图找到一种方法来整齐地写这个。

任务

在反应式编程中,处理结束拨打Concurrency 没有什么特别的问题。让我们看一个例子。

我准备了一个简单的Concurrency 方法。

。迅速
func calculate(number: Int) async -> Int {
    return number + 2
}

我会打电话给这个。

[示例 1]

。迅速
Just(10)
    .map { $0 / 2 }
    .sink { number in 
        Task {
            let result = await calculate(number: number)
            print(result) // 「7」が出力される
        }
    }
    .store(in: &cancelables)

它运行良好。困扰我的是每次都得写Task,而且会嵌套,所以我想把这个写的整整齐齐。 (稍后会详细介绍。)

[示例 2]

如果在处理过程中处理Concurrency,则会出现问题。

。迅速
Just(10)
    .map { $0 / 2 }
    .handleEvents(receiveOutput: { number in
        Task {
            let result = await task(number: number)
            return result // エラーになる
        }
    })
    .sink { number in 
        print(number)
    }
    .store(in: &cancelables)

不能在中间处理这个过程并传递给后续。
因此,我们将通过隐藏处理来解决这个问题。

执行

RxSwift 已经有一个名为 RxConcurrency 的库,所以我们也将实现 Combine

1. 查看 RxSwift 中的实现

库实现的摘录如下:

。迅速
extension ObservableType {

    static func async(_ handler: @escaping () async throws -> Element) -> Observable<Element> {
        Observable<Element>.create { observer in
            let task = Task {
                do {
                    observer.on(.next(try await handler()))
                    observer.on(.completed)
                } catch {
                    observer.on(.error(error))
                }
            }
            
            return Disposables.create {
                task.cancel()
            }
        }
    }
}

它所做的很简单,它在内部执行Observable create 并包装Coucurrency 处理。

这是它在实际使用中的样子。

。迅速
Observable<Int>
    .async {
        await self.calculate(number: 10)
    }
    .subscribe { number in
        print(number) // 「12」が出力される
    }
    .disposed(by: disposeBag)

这样,您就可以在处理过程中处理Coucurrency

2.在Combine中的实现

我将使用Combine 来实现它。然而,CombineRxSwift 中没有与create 等效的运算符因此,不能像以前那样实施。

如果是Combine的扩展库组合分机如果你使用 ,你有create 操作符,所以你可以用同样的方式扩展它。

- 使用 CombineExt

我们将使用下面的create 运算符对其进行扩展。

现在让我们看一下实现。

。迅速
extension Publishers {
        
    static func async<Output>(_ handler: @escaping () async -> Output) -> AnyPublisher<Output, Never> {
        AnyPublisher<Output, Never>.create { subscriber in
            let task = Task {
                let result = await handler()
                subscriber.send(result)
                subscriber.send(completion: .finished)
            }

            return AnyCancellable {
                task.cancel()
            }
        }
    }
}

可以看到,它的结构和前面给出的RxSwift的扩展码是一样的。

而且,在实际使用中,它看起来是这样的。

。迅速
Publishers
    .async {
        await self.calculate(number: 10)
    }
    .sink { number in
        print(number) // 「12」が出力される
    }
    .store(in: &cancellable)

像这样很容易实现。

这次它使用Publishers(和static func)扩展,但也可以单独扩展每个。例如,以下是Just 的扩展示例。

。迅速
extension Just {
    
    func async<Input, Output>(_ handler: @escaping (Input) async -> Output) -> AnyPublisher<Output, Never> {
        AnyPublisher<Output, Never>.create { subscriber in
            let task = Task {
                let result = await handler(output as! Input) // ※注意
                subscriber.send(result)
                subscriber.send(completion: .finished)
            }

            return AnyCancellable {
                task.cancel()
            }
        }
    }
}

* output属性是从Just流出的值,需要用值类型进行强制转换。

这是它在实际使用中的样子。

。迅速
Just<Int>(10)
    .async { number in
        await self.calculate(number: number)
    }
    .sink { number in
        print(number) // 「12」が出力される
    }
    .store(in: &cancellable)

这样,您可以根据需要准备一些扩展来灵活地实现它。然而,正如开头所说,是一个扩展库组合分机只能使用实现是。

现在,让我们考虑一个不使用扩展库的方法。

- 不使用 CombineExt 时

您必须要么实现前面提到的create 运算符,要么准备一些行为类似的东西。

我应该将create 的代码复制到扩展库CombineExt 中吗?你可能会想,但是依赖项出乎意料的多,所以有必要拉一些代码,如果你自己实现它到最低限度,代码会更少。.

使用flatMapFuture 进行扩展。


(1) 不需要错误处理的扩展
。迅速
extension Publisher {
    
    func asyncMap<V>(
        _ asyncFunction: @escaping (Output) async -> V
    ) -> Publishers.FlatMap<Future<V, Never>, Self> {
        
        flatMap { value in
            Future { promise in
                Task {
                    promise(.success(await asyncFunction(value)))
                }
            }
        }
    }
}

这是它在实际使用中的样子。

。迅速
Just<Int>(20)
    .asyncMap { number in
        await self.calculate(number: number)
    }
    .sink { number in
        print(number) // 「22」が出力される
    }
    .store(in: &cancellable)

(例如Just,但可以使用Publisher。)


(2) 可以处理错误的扩展

如果您想要错误处理,请进一步扩展前一个。

。迅速
extension Publisher {

    func asyncMapWithThrows<V>(
        _ asyncFunction: @escaping (Output) async throws -> V
    ) -> Publishers.FlatMap<Future<V, Error>, Publishers.SetFailureType<Self, Error>> {
        
        flatMap { value in
            Future { promise in
                Task {
                    do {
                        let output = try await asyncFunction(value)
                        promise(.success(output))
                    } catch {
                        promise(.failure(error))
                    }
                }
            }
        }
    }
}

这是它在实际使用中的样子。

。迅速
let subject = PassthroughSubject<(), Never>()

subject
    .asyncMapWithThrows {
        try await APIClient.fetch()
    }
    .sink(receiveCompletion: { result in
        // handle result
    }, receiveValue: { value in
        // handle value
    })
    .store(in: &cancellable)

subject.send(())

Publisher 中间打API 是一种模式。

您将能够像这样无缝地执行Combine -> Concurrency -> Combine


- 补充

顺便说一句,可以将TaskPriority作为参数传递,但是与Combine.subscribe(on: )冲突,所以无法设置。
CombineExt的PR中也提出了类似的观点,由于RxConcurrency被设计成不传参数,所以采用)


3. 额外

减少任务嵌套

困扰我的是每次都得写Task,最后都是嵌套,所以想把这个写的整齐一点。

这是您要消除开头提到的Task 的嵌套的时候。

正如我在开始时写的那样简单地做Combine -> Concurrency

。迅速
Just(10)
    .map { $0 / 2 }
    .sink { number in 
        Task {
            do {
                // do some async task
            } catch {
                // error handling
            }
        }
    }
    .store(in: &cancelables)

结果sinkTask嵌套了一层,在进行错误处理时,do catch变得更深。因此,我们将准备一个包装函数来解决它。


① 当失败永远不会
。迅速
extension Publisher where Self.Failure == Never {

    func asyncSink(
        receiveValue: @escaping ((Self.Output) async -> Void)
    ) -> AnyCancellable {
        self.sink { value in
            Task {
                await receiveValue(value)
            }
        }
    }
}

这是它在实际使用中的样子。

。迅速
Just<Int>(30)
    .asyncSink { number in
        let result = await self.calculate(number: number)
        print(result) // 「32」が出力される
    }
    .store(in: &cancellable)

它非常干净。


(2) 当失败为错误时
。迅速
extension Publisher where Self.Failure == Error {

    func asyncSinkWithThrows(
        receiveCompletion: @escaping ((Subscribers.Completion<Self.Failure>) async -> Void),
        receiveValue: @escaping ((Self.Output) async -> Void)
    ) -> AnyCancellable {
        sink(receiveCompletion: { result in
            Task { await receiveCompletion(result) }
        }, receiveValue: { value in
            Task { await receiveValue(value) }
        })
    }
}

这是它在实际使用中的样子。

。迅速
let subject = PassthroughSubject<(), Never>()

subject
    .setFailureType(to: Error.self)
    .asyncSinkWithThrows(receiveCompletion: { result in
        // handling result
    }, receiveValue: {
        let response = try await APIClient.fetch()
        // handling response
    })
    .store(in: &cancellable)

subject.send(())

这次是用没有错误的模式完成的,但即使receiveValue 也可以实现Concurrency

在最后

好久不见,我在这里创建了一个实现各种东西的库,所以希望你能轻松使用它!
我们正在等待额外的扩展和公关!

请给星!

其他

附加扩展

RxConcurrency 有其他扩展实现,因此最好参考它们。

参考


原创声明:本文系作者授权爱码网发表,未经许可,不得转载;

原文地址:https://www.likecs.com/show-308632009.html

 
反对 0举报 0 评论 0
 

免责声明:本文仅代表作者个人观点,与乐学笔记(本网)无关。其原创性以及文中陈述文字和内容未经本站证实,对本文以及其中全部或者部分内容、文字的真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
    本网站有部分内容均转载自其它媒体,转载目的在于传递更多信息,并不代表本网赞同其观点和对其真实性负责,若因作品内容、知识产权、版权和其他问题,请及时提供相关证明等材料并与我们留言联系,本网站将在规定时间内给予删除等相关处理.

  • iOS扩展——Objective-C开发编程规范 简单io扩展实验代码
    iOS扩展——Objective-C开发编程规范 简单io扩
      最近准备开始系统学习一个完整项目的开发流程和思路,在此之前,我们需要对iOS的开发变成规范进行更系统和详尽的学习,随意对编程规范进行了整理和学习。本文内容主要转载自:Objective-C-Coding-Guidelines-In-Chinese  此外,这篇文章所说的一些常见
    03-08
  • iOS打包framework - Swift完整项目打包Framework,嵌入OC项目使用
    iOS打包framework - Swift完整项目打包Framewor
    场景说明:-之前做的App,使用Swift框架语言,混合编程,内含少部分OC代码。-需要App整体功能打包成静态库,完整移植到另一个App使用,该App使用OC。-所以涉及到一个语言互转的处理,以及一些AppDelegate的代码减除变化。 --------------------------------
    02-09
  • iOS8 蓝牙设备的重连接(retrieve) Swift实现
      今天App写到了蓝牙重连的阶段,以前针对sdk 6.0写的代码,蓝牙设备的回复是通过- (void)retrievePeripherals:(NSArray *)peripheralUUIDs然后回调 centralManager:didRetrievePeripherals:函数来得到可以回复设备的Array。在SDK7之后,- (void)retrievePe
    02-09
  • iOS高效开发必备的10款Objective-C类库 object c ios
    iOS高效开发必备的10款Objective-C类库 object
    因为iOS SDK相对比较底层,所以开发者就得受累多做一些体力活。不过幸运的是,有很多第三方的类库可以用来简化很多不必要的工作.经过作者团队的慎重讨论,他们评选出了10款能够极大提高iOS开发效率的类库,根据原文作者的评价来看,基本上有了这10款工具,做i
    02-09
  • IOS基础:Objective-C 字符串处理
    //一、NSString/*----------------创建字符串的方法----------------*/ //1、创建常量字符串。NSString *astring = @"This is a String!";//2、创建空字符串,给予赋值。NSString *astring = [[NSString alloc] init];astring = @"This is a String!";[astri
    02-09
  • 微信小程序ios下禁用默认滚动事件 微信小程序ios下禁用默认滚动事件提醒
    微信小程序ios下禁用默认滚动事件 微信小程序io
    在微信小程序ios下,如果有一个scroll-view滚动列表,开始触摸点点在滚动列表外,向列表里拖拽列表是不会滚动的,这很正常,因为开始触摸点点在了滚动列表外。可是不正常的是ios下会出现“反应不过来”的现象,这很难表述,看下图图中view:A用了fixed主要是
    02-09
  • 小程序camera组件ios上出现授权的问题
    camera需要调用手机系统的摄像头,正常进入页面就会询问用户权限。但是ios上面,前提是已经打开了摄像头的权限,加载之后隐藏掉camera,再次显示的时候,就会进入binderror事件,该事件是当用户未打开摄像头权限才会进入的监听事件,尝试了用wx.openSetting再
    02-09
  • 【小程序】使用uni-app搭建小程序环境---时间格式问题 new Date(str) IOS系统跟Android系统不兼容
    【小程序】使用uni-app搭建小程序环境---时间格
    今天做了一个需求,要在列表中把后台返回来的时间给显示出来,使用 new Date(str)  在微信开发者工具上显示是没有问题的,然后在IOS系统上显示是NAN。原因是 IOS系统只识别 " / " 不识别 " - ". 后台返回来的时间类型一般有三种 时间、时间搓、字符串。我
    02-09
  • Delphi xe7 FireMonkey / Mobile (Android, iOS)生成 QR Code完整实例
    Delphi xe7 FireMonkey / Mobile (Android, iOS
    这个实例在windows、OS X、IOS和Android等平台运行正常。本文参考这个网站提供的方法:http://zarko-gajic.iz.hr/firemonkey-mobile-android-ios-qr-code-generation-using-delphi-xe-5-delphizxingqrcode/代码中用到的DelphiZXingQRCode.Pas点这下载1 unit U
    02-09
  • 预热篇- 总结Delphi Xe4 做App的的可行性分析.
    首先澄清一个问题, 很多同学其实是误会了, 以为只要搞定了Delphi 就能很快写快餐程序了.  ios 本身的知识还是需要一些的, 并没有什么捷径可以走. 但如果一个团队有分工协作的话, DelphiXe4 也可以考虑作为一种技术方向.  用对了地方, 就可以发挥Delphi的长
    02-09
点击排行