【问题标题】:Swift Combine transform an array of publishersSwift Combine 转换一组发布者
【发布时间】:2021-05-25 14:55:38
【问题描述】:

我正在通过组合位我似乎并没有围绕一些琐碎的操作。我正在构建一个基于组合来执行一些 HealthKit 操作。主要我有两个操作:

  1. func workouts(_ limit: Int) -> AnyPublisher<[HKWorkout], Error>
  2. func workoutDetails(_ workout: HKWorkout) -> AnyPublisher<WorkoutDetails, Error>

首先我会使用第一个来查询一些锻炼,这很容易。

HKHealthStore().workouts(HKObjectQueryNoLimit)
    .sink(receiveCompletion: { subject in
        switch subject {
        
        case .finished:
            break
        case .failure(let error):
            dump(error)
        }
    }, receiveValue: { workouts in
        dump(workouts)
        // What now? 
    }).store(in: &bag)  

现在我想查询第二个函数。它将锻炼作为参数并返回有关HKWorkout 的更多信息。如何使用返回的 [HKWorkout] 查询第二个函数,但不知道如何处理。

  • 是否应该在第一种方法的 sink 部分以嵌套方式进行查询?
  • 有没有办法在[HKWorkout] 中为每个锻炼嵌套操作,并为每个锻炼调用workoutDetails(_ workout: HKWorkout),以便我得到[WorkoutDetails] 的结果?

到目前为止我所做的尝试都不起作用。

HKHealthStore().workouts(HKObjectQueryNoLimit)
    .map({ workouts -> AnyPublisher<[WorkoutDetails], Never> in
        
        return workouts.publisher.flatMap({ workout in
            return Just(workout as! [WorkoutDetails]).eraseToAnyPublisher()
                .eraseToAnyPublisher()
        })
        .eraseToAnyPublisher()
        
    }).sink(receiveCompletion: { subs in
        switch subs {
        
        }
    }, receiveValue: { val in
        /// Cannot convert value of type 'AnyPublisher<[WorkoutDetails], Never>' to expected argument type 'Void'
        /// I am not able to get [WorkoutDetails] here as I'd like
        dump(val)
    })
    .store(in: &bag)

编辑:我终于使用的解决方案。有两个选项我终于设法让它发挥作用并理解它。

一个是@New Dev 的答案是最终在sink 组合数组中接收,另一个是接收sink 单个元素以保存在数组中。

HKHealthStore().workouts(HKObjectQueryNoLimit)
    .flatMap({
        $0.publisher.eraseToAnyPublisher()
    }).flatMap({
        $0.workoutWithDetails
    }).sink(receiveCompletion: { comp in
        switch comp {

        case .finished:
            break
        case .failure(let er):
            dump(er)
        }
    }, receiveValue: { details in
        /// Add the individual `WorkoutDetails` elements to an array 
        results.append(details)
    }).store(in: &SGHealthManager.bag)

【问题讨论】:

标签: swift combine


【解决方案1】:

一般的做法是把flatMap数组变成Sequence各个值的发布者,然后对每个值(另一个flatMap)做任何异步操作,然后collect得到结果:

HKHealthStore()
   .workouts(HKObjectQueryNoLimit)
   .flatMap { workouts in workouts.publisher }
   .flatMap { workout in
       workoutDetails(workout)
   }
   .collect()
   .sink(
      receiveCompletion: { ... },
      receiveValue: { arrayOfWorkoutDetails in
          // arrayOfWorkoutDetails is of type [WorkoutDetails]
      })
   .store(in: &bag)

【讨论】:

  • 非常感谢您的解释。我已经通过修改编辑了我的答案,该修改将推送接收器的各个值,而不是使用 collect 等到所有发布者都已处理完毕。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-08-21
  • 1970-01-01
  • 2019-11-08
  • 2021-07-10
  • 2020-11-06
相关资源
最近更新 更多