// // ListService.swift // HealthyZG // // Created by 林 on 2020/5/29. // Copyright © 2020 Lin. All rights reserved. // import UIKit import RxSwift import Moya import ObjectMapper import RxDataSources import RxSwiftExt protocol ListServiceType { associatedtype BaseListModel where BaseListModel: Mappable associatedtype BaseItem where BaseItem: Mappable } typealias PagingTargetTransform = (Int) -> MultiTarget enum PagingRequestType { case refresh case more case clear } typealias RefreshResult = (status: RefreshStatus, isEmpty: Bool) /// ListType: 列表数据泛型类,例: ListService> class ListService: ListServiceType where ListType: ListModelType, ListType.Item: Mappable { typealias BaseListModel = ListType typealias BaseItem = ListType.Item struct ListResponse { var listModel: BaseListModel? var items: [BaseItem] = [] var pagination: PaginationModel? var refreshStatus: RefreshStatus? } private var page = 1 private var isPaging = true var targetTransform: PagingTargetTransform var request = PublishSubject() var refreshResult: Observable { let refreshResult = _listResponse.flatMap { listResponse -> Observable in guard let refreshStatus = listResponse.refreshStatus else { return Observable.empty() } return Observable.just((refreshStatus, listResponse.items.isEmpty)) } return Observable.merge(refreshResult, _refreshResult) } var listResponse: Observable { return _listResponse } var response: Observable<[BaseItem]> { let itemResponse = _listResponse.map { $0.items } return Observable.merge(itemResponse, _localResponse) } var responseModel: Observable { return _listResponse.flatMap { listResponse -> Observable in guard let listModel = listResponse.listModel else { return Observable.empty() } return Observable.just(listModel) } } var pagination: Observable { return _listResponse.map { $0.pagination } } var error: Observable { return _error.asObserver() } var executing: Observable { return _executing.asObserver() } var isExecuting = false var listResponseModel: ListResponse? var listModel: BaseListModel? { return listResponseModel?.listModel } var paginationModel: PaginationModel? { return listResponseModel?.pagination } var items: [BaseItem] { return listResponseModel?.items ?? [] } private var _listResponse: Observable = .empty() private let _localResponse = PublishSubject<[BaseItem]>() private let _refreshResult = PublishSubject() private let _error = PublishSubject() private let _executing = PublishSubject() // MARK: API var fileterMap: ((PagingRequestType) -> FilterMap) { return { [unowned self] request -> FilterMap in switch request { case .refresh: // 下拉刷新 self.page = 1 self.isExecuting = true self._executing.onNext(self.isExecuting) return .map(self.page) case .more: // 上拉加载更多 if self.items.isEmpty { self.page = 1 } self.page += 1 self.isExecuting = true self._executing.onNext(self.isExecuting) return .map(self.page) case .clear: // 清空数据 self.onNext([]) return .ignore } } } var transform: ((Event) -> ListResponse) { return { [weak self] (event) in var listResponse = ListResponse() if let items = self?.listResponseModel?.items { listResponse.items = items } guard let self = self else { return listResponse } switch event { case let .next(newListModel): listResponse.listModel = newListModel // New data arrived let newItems = newListModel.list // Paging enable if self.isPaging { if self.page == 1 { if newItems.isEmpty { listResponse.items = [] } else { listResponse.items = newItems } } else { listResponse.items.append(contentsOf: newItems) } // Paging info listResponse.pagination = newListModel.pagination if newListModel.pagination!.hasMoreData() { listResponse.refreshStatus = .hasMoreData } else { listResponse.refreshStatus = .noMoreData } } else { if newItems.isEmpty { listResponse.items = [] } else { listResponse.items = newItems } listResponse.refreshStatus = .noMoreData } default: break } self.listResponseModel = listResponse return listResponse } } var filter: ((Event) -> Bool) { return { [weak self] (event) in guard let self = self else { return false } switch event { case let .error(error): if self.page != 1 { self.page -= 1 } self.isExecuting = false self._executing.onNext(self.isExecuting) self._refreshResult.onNext((.invalidData, self.items.isEmpty)) self._error.onNext(error) return false case .completed: return false case .next: self.isExecuting = false self._executing.onNext(self.isExecuting) return true } } } @available(*, deprecated, message: "将弃用,请使用init(newPaging: Bool = true, targetTransform: @escaping PagingTargetTransform)") init(paging: Bool = true, targetTransform: @escaping PagingTargetTransform) { self.isPaging = paging self.targetTransform = targetTransform _listResponse = request.filterMap(self.fileterMap) .map { [weak self] page -> MultiTarget in guard let self = self else { return targetTransform(page) } return self.targetTransform(page) }.flatMapLatest { [weak self] api -> Observable in guard let self = self else { return Observable.empty() } return self.request(api: api) .materialize() .filter(self.filter) .map(self.transform) }.share(replay: 1) } init(newPaging: Bool = true, targetTransform: @escaping PagingTargetTransform) { self.isPaging = newPaging self.targetTransform = targetTransform _listResponse = request.filterMap(self.fileterMap) .map { [weak self] page -> MultiTarget in guard let self = self else { return targetTransform(page) } return self.targetTransform(page) }.flatMapLatest { [weak self] api -> Observable in guard let self = self else { return Observable.empty() } return self.requestList(api: api) .materialize() .filter(self.filter) .map(self.transform) }.share(replay: 1) } func request(api: MultiTarget) -> Observable { if self.isPaging == false { return APIProvider.request(token: api) .map(ListType.self) .asObservable() } return APIProvider.request(token: api) .map(ListType.self)//, atKeyPath: "result") .asObservable() } func requestList(api: MultiTarget) -> Observable { if self.isPaging == false { return APIProvider.request(token: api).map(ListType.self).asObservable() } return APIProvider.request(token: api).map(ListType.self).asObservable() } func onNext(_ value: [BaseItem]) { listResponseModel?.items = value _localResponse.onNext(value) } } // MARK: - Map array elements to animatable model extension Array where Element: IdentifiableType & Equatable { func mapAnimatableSection() -> [AnimatableSectionModel] { guard !isEmpty else { return [] } let newItems = removedDuplicates() return [AnimatableSectionModel(model: "", items: newItems)] } } extension Array where Element: IdentifiableType { func removedDuplicates() -> [Element] { // remove duplicated item var uuids = Set() var newItems: [Element] = [] for item in self { let identity = item.identity if !uuids.contains(identity) { _ = uuids.insert(identity) newItems.append(item) } } return newItems } } // MARK: - Map array elements to section model extension Array { func mapSection() -> [SectionModel] { guard !isEmpty else { return [] } return [SectionModel(model: "", items: self)] } } // MARK: - Differ animatable section model extension ListService where BaseItem: IdentifiableType & Equatable { var animatableSectionedItems: Observable<[AnimatableSectionModel]> { return response.map { items -> [AnimatableSectionModel] in return items.mapAnimatableSection() } } } // MARK: - Section model extension ListService { var sectionedItems: Observable<[SectionModel]> { return response.map { items -> [SectionModel] in return items.mapSection() } } }