美文网首页
RxSwift + MVVM

RxSwift + MVVM

作者: 文子飞_ | 来源:发表于2020-12-25 18:21 被阅读0次

定义单独的 ViewModel 加工 Model,并把适合展示的数据输出给 View

软件设计有个重要的原则——封装变化,分析前面两个方案的局限性,我们可以明确的知道,“数据加工”是一个比较容易变化的点,而 View 和 Model 相对来说要稳定一些。那我们就把“数据加工”这个逻辑单独封装起来,把变化的部分和不变的部分隔离,这样 View 和 Model 的复用性就都提高了。

protocol ViewModelType {
    var avatarURL: NSURL { get }
    var followers: AttributedTitle { get }
    var repositories: AttributedTitle { get }
    var following: AttributedTitle { get }
    var nickname: String { get }
    var bio: String { get }
}


struct ProfileHeaderViewModel: ViewModelType {

    // MARK: Output
    let avatarURL: NSURL
    let followers: AttributedTitle
    let repositories: AttributedTitle
    let following: AttributedTitle
    let nickname: String
    let bio: String

    init(input: Profile) {
        avatarURL = NSURL(string: input.avatar) ?? defaultImageURL

        nickname = input.login

        bio = input.bio ?? defailtProfileItem

        followers = (title: "\(input.followers) \n\(attributingFollowers)", attributingTitle: attributingFollowers)

        repositories = (title: "\(input.repos) \n\(attributingRepos)", attributingTitle: attributingRepos)

        following = (title: "\(input.following) \n\(attributingFollowing)", attributingTitle: attributingFollowing)
    }
}

这个 ViewModel 以一个 Model 为输入,以一些可以直接被 View 使用的数据为输出。然后我们把它注入到 View 中即可,注入的方式无所谓,无论是作为初始化参数,抑或是作为属性或者方法参数等等,都可以,只要它是能被外部注入的,而不是由 View 自己生成的即可。譬如把它作为属性:

var viewModel: ViewModelType! {
    didSet {
        nicknameLabel.text = viewModel.nickname

        configAvatar(viewModel.avatarURL)

        bioLabel.text = viewModel.bio

        configButtons(viewModel.followers, viewModel.repositories, viewModel.following)
    }
}

RxSwift + MVVM

protocol ViewModelType {
    var avatarURL: Driver<NSURL> { get }
    var followers: Driver<AttributedTitle> { get }
    var repositories: Driver<AttributedTitle> { get }
    var following: Driver<AttributedTitle> { get }
    var nickname: Driver<String> { get }
    var bio: Driver<String> { get }
}

struct ProfileHeaderViewModel: ViewModelType {

    // MARK: Output
    let avatarURL: Driver<NSURL>
    let followers: Driver<AttributedTitle>
    let repositories: Driver<AttributedTitle>
    let following: Driver<AttributedTitle>
    let nickname: Driver<String>
    let bio: Driver<String>

    init(input: Profile) {
        let profileDriver = Driver.of(input)
        avatarURL = profileDriver
            .map { $0.avatar }
            .map { NSURL(string: $0) ?? defaultImageURL }

        followers = profileDriver
            .map { (title: "\($0.followers) \n\(attributingFollowers)", attributingTitle: attributingFollowers) }

        repositories = profileDriver
            .map { (title: "\($0.repos) \n\(attributingRepos)", attributingTitle: attributingRepos) }

        following = profileDriver
            .map { (title: "\($0.following) \n\(attributingFollowing)", attributingTitle: attributingFollowing) }

        nickname = profileDriver
            .map { $0.login }

        bio = profileDriver
            .map { $0.bio ?? defailtProfileItem }
    }
}

class ProfileHeader: UIView {
    // ...

    private let bag = DisposeBag()
    var viewModel: ViewModelType! {
        didSet {
            viewModel.nickname
                .drive(nicknameLabel.rx_text)
                .addDisposableTo(bag)

            viewModel.avatarURL
                .drive(onNext: configAvatar)
                .addDisposableTo(bag)

            viewModel.bio
                .drive(bioLabel.rx_text)
                .addDisposableTo(bag)

            Driver.zip(viewModel.followers, viewModel.repositories, viewModel.following) { ($0.0, $0.1, $0.2) }
                .drive(onNext: configButtons)
                .addDisposableTo(bag)
        }
    }

    // ...
}

相关文章

网友评论

      本文标题:RxSwift + MVVM

      本文链接:https://www.haomeiwen.com/subject/vmsinktx.html