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

Posted

技术标签:

【中文标题】使用 RxSwift 在网络请求后更新 SwiftUI 列表【英文标题】:Update SwiftUI List after network request using RxSwift 【发布时间】:2019-12-20 12:28:35 【问题描述】:

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

简单的联系人列表:

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"))
        
    

然后是视图模型

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
    

和网络模型

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】:

虽然我找到了一些组合解决方案。 仍在等待 Rx 解决方案。 所以,我修复了它,只是为 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)
           
        
    

【讨论】:

以上是关于使用 RxSwift 在网络请求后更新 SwiftUI 列表的主要内容,如果未能解决你的问题,请参考以下文章

RXSwift的一些基本交互(OC,Swift,RXSwift对比)

swift常用第三方库

未调用 RxSwift 订阅块

Swift 4“调用中的额外参数”Rxswift

RxSwift,分享+重试机制

RxSwift 更新身份验证令牌