使用RxSwift发出网络请求后更新SwiftUI列表

时间:2019-12-20 12:28:35

标签: rx-swift moya swiftui-list

发出网络请求后,我需要更新SwiftUI列表。对于请求,我将Moya方法与触发器(Input&Output-“ Kickstarter”)组合使用。 由于项目的结构,我不能使用Combine框架,尽管他们有很多有用的建议(不确定我的情况)。

简单的联系人列表:

struct ContactList: View {
    var viewModel: UserViewModel
    var body: some View {
        NavigationView {
            List(viewModel.users) { contact in
                NavigationLink(destination: ContactDetail(user: contact)) {
                    ContactRow(user: contact)
                }
            }
            .navigationBarTitle(Text("Team Members"))
        }
    }
}

然后使用ViewModel

class UserViewModel {
    let disposeBag = DisposeBag()
    var users: [TeamMember] = []

    init(users: [TeamMember] = []) {
        let networkModel = UserNetworkModel()
        networkModel.output.teamMembers.subscribe { (event) in
            self.users.append(contentsOf: event.element.orEmpty)
        }.disposed(by: disposeBag)

        networkModel.output.error.subscribe(onNext: { error in
            print(error.localizedDescription)
        }).disposed(by: disposeBag)
        networkModel.input.loadTrigger.onNext(Void())
        self.users = users
    }
}

和NetworkModel

class UserNetworkModel {
    let disposeBag = DisposeBag()
    let input: Input
    let output: Output

    struct Input {
        let loadTrigger: AnyObserver<Void>
        let searchTrigger: AnyObserver<String>
    }

    struct Output {
        let teamMembers: Observable<[TeamMember]>
        let error: Observable<Error>
    }


    internal let loadSubject = PublishSubject<Void>()
    internal let searchSubject = BehaviorSubject<String>(value: "")

    internal let errorSubject = PublishSubject<Error>()
    internal let teamMembersSubject = BehaviorSubject<[TeamMember]>(value: [])

    init() {
        let service = MoyaProvider<TeamTarget>()
        self.input = Input(loadTrigger: loadSubject.asObserver(), searchTrigger: searchSubject.asObserver())
        self.output = Output(teamMembers: teamMembersSubject.asObservable(), error: errorSubject.asObservable())

        let result = loadSubject.flatMapLatest { _ -> Observable<[TeamMember]> in
            service.rx.request(.get).debug().mapArray(TeamMember.self).asObservable()
        }.share(replay: 1)

        Observable.combineLatest(result, searchSubject).map { (arg) in
            let (members, filter) = arg
            if filter.isEmpty {
                return members
            } else {
                let searchText = try! self.searchSubject.value()
                return members.filter({
                    return [$0.firstName, $0.lastName]
                        .compactMap({ $0 })
                        .first(where: { $0.hasPrefix(searchText) }) != nil
                })
            }
        }.bind(to: teamMembersSubject).disposed(by: disposeBag)

        result.subscribe(onError: { error in
            self.errorSubject.onNext(error)
        }).disposed(by: self.disposeBag)

    }
}

是否可以通过这种方式更新用户数组?还是只有Combine才能为我轻松做到? 感谢您的宝贵时间。

1 个答案:

答案 0 :(得分:0)

虽然我找到了一些Combine解决方案。 仍在等待接收解决方案。 因此,我修复了此问题,仅在Network模型中为ContactList属性添加了@ObservedObject,为用户数组添加了@Published。更少的代码,本机但不是我想要的。

完整答案:

struct ContactList: View {
    @ObservedObject var networkModel: NetworkModel
    var body: some View {
        NavigationView {
            List(networkModel.users) { contact in
                NavigationLink(destination: ContactDetail(user: contact)) {
                    ContactRow(user: contact)
                }
            }
            .navigationBarTitle(Text("Team Members"))
        }
    }
}

#if DEBUG
struct ContactList_Previews: PreviewProvider {
    static var previews: some View {
        ContactList(networkModel: .init(users: contactData))
    }
}
#endif

class NetworkModel: ObservableObject {
    @Published var users = [User]()

    init(users: [User] = []) {
        getMembers()
    }

    private func getMembers() {
        let provider = MoyaProvider<TeamTarget>()
        provider.request(.get) { [weak self] (result) in
           switch result {
           case .success(let response):
                do {
                   let result = try JSONDecoder().decode([User].self, from: response.data)
                   self?.users.append(contentsOf: result)
                } catch {
                   print(error.localizedDescription)
                }
           case .failure(let error):
                print(error.errorDescription.orEmpty)
           }
        }
    }
}