【问题标题】:RxSwift correct way to subscribe to data model property changeRxSwift 订阅数据模型属性更改的正确方法
【发布时间】:2018-08-19 03:44:34
【问题描述】:

这里是 RxSwift 的新手。我有一个(MVVM)视图模型,它代表一个类似新闻源的页面,订阅数据模型属性更改的正确方法是什么?在以下示例中,startUpdate() 不断更新post。计算出的属性 messageToDisplayshouldShowHeart 驱动一些 UI 事件。

struct Post {
    var iLiked: Bool
    var likes: Int
    ...
}

class PostViewModel: NSObject {
    private var post: Post

    var messageToDisplay: String {
        if post.iLiked { return ... }
        else { return .... }
    }

    var shouldShowHeart: Bool {
        return iLiked && likes > 10
    }

    func startUpdate() {
        // network request and update post
    }
    ...
}

在我看来,为了使整个事情具有反应性,我必须将Post 的每个属性和所有计算属性都转换为Variable?我觉得不太对劲。

【问题讨论】:

    标签: ios swift mvvm rx-swift


    【解决方案1】:

    // 类 NetworkRequest 或任何名称

    static func request(endpoint: String, query: [String: Any] = [:]) -> Observable<[String: Any]> {
        do {
            guard let url = URL(string: API)?.appendingPathComponent(endpoint) else {
                throw EOError.invalidURL(endpoint)
            }
    
            return manager.rx.responseJSON(.get, url)
                .map({ (response, json) -> [String: Any] in
                    guard let result = json as? [String: Any] else {
                        throw EOError.invalidJSON(url.absoluteString)
                    }
                    return result
                })
          } catch {
            return Observable.empty()
          }
    }
    
    static var posts: Observable<[Post]> = {
        return NetworkRequest.request(endpoint: postEndpoint)
            .map { data in
                let posts = data["post"] as? [[String: Any]] ?? []
                return posts
                    .flatMap(Post.init)
                    .sorted { $0.name < $1.name }
            }
            .shareReplay(1)
    }()
    
    
    class PostViewModel: NSObject {
        let posts = Variable<[Post]>([])
        let disposeBag = DisposeBag()
        override func viewDidLoad() {
          super.viewDidLoad()
    
          posts
           .asObservable()
           .subscribe(onNext: { [weak self] _ in
             DispatchQueue.main.async {
                //self?.tableView?.reloadData() if you want to reload all tableview
                self.tableView.insertRows(at: [IndexPath], with: UITableViewRowAnimation.none) // OR if you want to insert one or multiple rows.
                //OR update UI
             }
          })
          .disposed(by: disposeBag)
    
    
    
          posts.asObservable()
         .bind(to: tableView.rx.items) { [unowned self] (tableView: UITableView, index: Int, element: Posts) in
            let cell = tableView.dequeueReusableCell(withIdentifier: "postCell") as! PostCell
    //for example you need to update the view model and cell with textfield.. if you want to update the ui with a cell then use cell.button.tap{}. hope it works for you.
            cell.textField.rx.text
                .orEmpty.asObservable()
                .bind(to: self.posts.value[index].name!)
                .disposed(by: cell.disposeBag)
            return cell
        }
    
          startDownload()
      }
    }
    
    func startDownload {
        posts.value = NetworkRequest.posts
    }
    

    如果您想更改任何内容,请使用 subscribe、bind、concat .. 您可以使用许多方法和属性。

    【讨论】:

    • 这如何解决我在帖子属性更改时更新 UI 的问题?
    • 当posts.value改变posts .asObservable() .subscribe(onNext: { [weak self] _ in 会自动调用。这是observable或rxswift的主要特性
    • 好的,我明白了,所以我无法只更新 post 的单个属性,而是必须更新整个内容?
    • 请查看我的更新答案。您要做的是先更新数组,然后使用以下行,无需重新加载整个 tableView。 self.tableView.insertRows(at: [IndexPath], with: UITableViewRowAnimation.none)
    • 这就是你找到你的索引路径的方法 let indexPath:IndexPath = IndexPath(row:(posts.count - 1), section:0)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-02-25
    • 1970-01-01
    • 2021-12-05
    • 1970-01-01
    相关资源
    最近更新 更多