【问题标题】:ReactiveCocoa 5, ReactiveSwift network sub-request handling & best practiceReactiveCocoa 5,ReactiveSwift 网络子请求处理和最佳实践
【发布时间】:2017-04-25 17:58:48
【问题描述】:

我正在尝试寻找最佳实践来处理从父请求中获得的每个值的多个子请求。我正在尝试使用与此处相同的逻辑 - Reactive Cocoa 5 and ReactiveSwift network requests handling,但有一些问题。

我们拥有和需要什么:
1.带有无限滚动处理程序的 TableView (SVPullToRefresh)
2.每次调用处理程序时获取对象列表
3.从响应中为每个对象发送“子请求”

注意事项:
1. 一旦 viewController 关闭(调用 deinit),所有请求(父 + 子请求)都应该被取消
2. 我需要有随时取消父请求的能力。这也应该取消所有子请求。

我目前拥有的

我知道我在“无限处理程序”中所做的有点像“胶带”,但我是 ReactiveSwift 的新手...

self.tableView.addInfiniteScrollingWithActionHandler { [unowned self] in
    self.tempMutableProperty.value = true
}

self.tempMutableProperty.producer.skipNil().flatMap(.latest) { [unowned self] tempValueThatIDontNeed in
    return self.producerForParentRequest(offset: self.offset)
        .take(during: self.reactive.lifetime)
        .on(
            // handlers for errors, completed, etc
            value: { [unowned self] items in
                self.items.append(items)
                self.tableView.reloadData()
                self.offset += items.count
                // SEND REQUEST #2 FOR EACH ITEM
            }
    ).flatMapError { error in
        return SignalProducer.empty
    }
}.observe(on: UIScheduler().start()

所以,如您所见,我使用 tableView 进行分页。我正在为每个页面获取对象列表。然后对于响应中的每个项目,我需要使用请求 #2 获取附加信息。


流程和问题:
1.我当然想摆脱tempMutableProperty,并以某种方式开始新的parent request,而不需要某种代理2. 每个sub-request 应该是独立的,这意味着我希望为每个sub-request 分别调用value/error 处理程序,而不是等待所有10 个子请求然后调用成功处理程序并收集所有 10 个响应。此外,某些特定子请求的失败不应影响其他正在运行的子请求
3.用户可以更改他的搜索请求,而无需等待整个请求过程完成。这意味着一旦用户更改了某些参数,我将清除所有项目,并且我需要在所有sub-requests 中取消parent request 并重新开始这一切。
4. 除了# 2,有时用户可以向下滚动以获取新的项目部分。这意味着新的parent request 应该开始,但来自parent request 先前响应的sub-requests 应该继续工作
5. 所有请求都应该在self.deinit 上被取消,所以这一切应该只在self.lifetime 期间有效,但我不确定放置此参数的正确位置是什么

如果不将一次性/信号存储为 self 的属性,我不确定这一切是否可行,所以如果 sub-request 会以某种方式存储为属性,这不是问题。


谢谢大家的帮助

【问题讨论】:

    标签: ios swift reactive-cocoa racsignal reactive-swift


    【解决方案1】:

    对于第 1 部分,我将添加一个扩展,将无限滚动动作处理转换为信号:

    extension Reactive where Base: UITableView {
        public func infiniteScrollingSignal() -> Signal<Void, NoError>
        {
            return Signal { [unowned base = self.base] observer in
                base.addInfiniteScrollingWithActionHandler  {
                    observer.send(value: ())
                }
    
                return ActionDisposable {
                    // Unsubscribe the infinite scrolling action handler here if necessary
                }
            }
            .take(during: self.lifetime)
        }
    }
    

    然后你可以将你所有的逻辑连接到self.tableView.reactive.infiniteScrollingSignal()

    【讨论】:

    • hmm... 这比我的解决方案好一点,因为我不需要存储任何类型的一次性属性。但它只是一个“化妆品问题”,就像它仍然发送虚拟布尔值true,只是为了调用flatMap闭包。无论如何,这是一个很好的建议,这将使 viewController 的代码更清晰
    • 我觉得你可以把类型从Signal&lt;Bool, NoError&gt;改成Signal&lt;Void, NoError&gt;,然后发送一个空元组值:observer.send(value: ())。我不确定我的头顶,所以我只使用了 Bool。
    • 另外,处理您不关心的参数的 Swift 方法是只使用下划线,例如 flatMap(.latest) { _ in ... }
    【解决方案2】:

    所以,我将在这里发布我的问题的解决方案。

    对于第 1 点我做了这个:

    let disposable = SerialDisposable()
    
    self.tableView.addInfiniteScrolling(actionHandler: { [unowned self] in
        self.disposable.inner = nil // this is needed to force dispose current request before starting new one
        self.disposable.inner = self.producer().take(during: self.reactive.lifetime)
            .on(value: { [unowned self] value in
                // handle as you want
            }).start()
    })
    

    这帮助我摆脱了tempMutableProperty。使用SerialDisposable 代替flatMap。 所以,这对我来说很好,它会在 self 被销毁时自动处理请求

    其他观点我做了这个:

    我的想法是我正在为表格加载一些项目,然后我需要为每个项目发送额外的请求以获取它的详细信息。所以,我创建了一个类,有 3 个属性 - itemitemDetailrequestSent

    然后在willDisplay cell我有

    func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
        if !self.items[indexPath.row].requestSent {
            self.items[indexPath.row].details <~ self.detailedProducerForItemID(self.items[indexPath.row].item.id)
            self.items[indexPath.row].requestSent = true
        }
    }
    

    注意self.items[indexPath.row].detailsMutableProperty&lt;Details?&gt;

    在表示细节的单元格本身中,我有类似的内容:

    let (detailsSignal, detailsObserver) = Signal&lt;MutableProperty&lt;Details?&gt;, NoError&gt;.pipe(),其中Details 是项目详细信息的类名称。

    在单元格的awakeFromNib:

    let details = self.detailsSignal.flatMap(.latest) { $0.producer }
    
    self.detailsLabel.reactive.text <~ details.map { value -> String? in
        // handling `Details`
    }
    

    cellForRow 我打电话给cell.detailsObserver.send(value: self.items[indexPath.row].details)

    而且,当调用 VC 的 deinit 或我执行新的 main 请求时,所有请求都会自动取消,因为当我使用 self.items.removeAll() 时,它会处理所有请求。

    这是粗流。如果有人对更多细节感兴趣,请不要犹豫。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2019-02-18
      • 1970-01-01
      • 1970-01-01
      • 2010-10-08
      • 2011-06-17
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多