ios - 结合使用 RxSwift 为同一对象获取不同属性的 2 个 API 调用的结果

标签 ios swift rx-swift moya

我有一个名为 Track 的模型。它具有一组基本属性和一组扩展属性。轨道列表及其基本属性是通过搜索 API 调用获取的,然后我需要使用这些轨道 ID 进行另一个 API 调用以获取它们的扩展属性。

问题是如何最好地结合两个 API 调用的结果,并将扩展属性填充到已经创建的 Track 对象中,当然还要通过 ID (不幸的是,这在两次调用的结果中都是不同的属性名称)。请注意,在实际结果集中返回了更多的键 - 两次调用中的每一次调用大约有 20-30 个属性。

Track.swift

struct Track: Decodable {

// MARK: - Basic properties

let id: Int
let title: String

// MARK: - Extended properties

let playbackURL: String

enum CodingKeys: String, CodingKey {
    case id = "id"

    case title = "title"

    case playbackUrl = "playbackUrl"
}

init(from decoder: Decoder) throws {
    let container = try decoder.container(keyedBy: CodingKeys.self)

    let idString = try container.decode(String.self, forKey: CodingKeys.id)
    id = idString.int ?? 0

    title = try container.decode(String.self, forKey: CodingKeys.title)

    playbackURL = try container.decodeIfPresent(String.self, forKey: CodingKeys.playbackUrl) ?? ""
}
}

ViewModel.swift

let disposeBag = DisposeBag()
var searchText = BehaviorRelay(value: "")
private let provider = MoyaProvider<MyAPI>()
let jsonResponseKeyPath = "results"

public lazy var data: Driver<[Track]> = getData()

private func searchTracks(query: String) -> Observable<[Track]> {
    let decoder = JSONDecoder()
    return provider.rx.request(.search(query: query))
        .filterSuccessfulStatusCodes()
        .map([Track].self, atKeyPath: jsonResponseKeyPath, using: decoder, failsOnEmptyData: false)
        .asObservable()
}

private func getTracksMetadata(tracks: Array<Track>) -> Observable<[Track]> {
    let trackIds: String = tracks.map( { $0.id.description } ).joined(separator: ",")
    let decoder = JSONDecoder()
    return provider.rx.request(.getTracksMetadata(trackIds: trackIds))
        .filterSuccessfulStatusCodes()
        .map({ result -> [Track] in

        })
        .asObservable()
}

private func getData() -> Driver<[Track]> {
    return self.searchText.asObservable()
        .throttle(0.3, scheduler: MainScheduler.instance)
        .distinctUntilChanged()
        .flatMapLatest(searchTracks)
        .flatMapLatest(getTracksMetadata)
        .asDriver(onErrorJustReturn: [])
}

.search API 调用的 JSON 结果结构如下:

{
  "results": [
    {
      "id": "4912",
      "trackid": 4912,
      "artistid": 1,
      "title": "Hello babe",
      "artistname": "Some artist name",
      "albumtitle": "The Best Of 1990-2000",
      "duration": 113
    },
    { 
      ...
    }
  ]
}

.getTracksMetadata API 调用的 JSON 结果结构如下:

[
  {
    "TrackID": "4912",
    "Title": "Hello babe",
    "Album": "The Best Of 1990-2000",
    "Artists": [
      {
        "ArtistID": "1",
        "ArtistName": "Some artist name"
      }
    ],
    "SomeOtherImportantMetadata1": "Something something 1",
    "SomeOtherImportantMetadata2": "Something something 2",
    "SomeOtherImportantMetadata3": "Something something 3"
  },
  { 
    ...
  }
]

最佳答案

这里的解决方案是一个两阶段的方法。首先,您应该为两个网络调用定义两个不同的结构,并为组合结果定义第三个结构。假设您选择:

struct TrackBasic {
    let id: Int 
    let title: String 
}

struct TrackMetadata {
    let id: Int // or whatever it's called.
    let playbackURL: String
}

struct Track {
    let id: Int 
    let title: String 
    let playbackURL: String
}

然后像这样定义你的函数:

func searchTracks(query: String) -> Observable<[TrackBasic]>
func getTracksMetadata(tracks: [Int]) -> Observable<[TrackMetadata]>

现在您可以进行这两个调用并将来自两个独立端点的数据包装到组合结构中:

searchText
    .flatMapLatest { searchTracks(query: $0) }
    .flatMapLatest { basicTracks in
        Observable.combineLatest(Observable.just(basicTracks), getTracksMetadata(tracks: basicTracks.map { $0.id }))
    }
    .map { zip($0.0, $0.1) }
    .map { $0.map { Track(id: $0.0.id, title: $0.0.title, playbackURL: $0.1.playbackURL) } }

上面假设轨道元数据的顺序与请求的顺序相同。如果不是这种情况,那么最后一张 map 将必须更复杂。

关于ios - 结合使用 RxSwift 为同一对象获取不同属性的 2 个 API 调用的结果,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53334778/

相关文章:

ios - 当用户触摸屏幕时 uiimageview 动画停止

ios - Swift 函数在完成处理之前返回值

ios - RxSwift 中 Disposables.create() 的目的

ios - 仅当使用 RxBluetoothKit 连接成功时才延长蓝牙连接超时

ios - 如何从 iOS 扩展中的图像 URL 获取对 PHAsset 的引用?

ios - 如何返回 enumerateObjectsUsingBlock 找到的项目?

iOS RestKit 问题 : Invalid parameter not satisfying: responseDescriptors

ios - 无法构建 Objective-C 模块 'zipzap'

ios - 如何从 UIActivityViewController 中排除 Action 事件?

swift - Rx swift : disposed(by:) weirdness