我正在尝试在我的应用中使用MVVM。我发现MVVM以不同的格式实现。因此,我应该使用哪种方法令人困惑。对于获取用户并在tableView中显示列表的情况,我有两种不同的实现。
模型定义为:
struct User: Decodable {
var id: String?
var name: String?
var email: String?
}
对于网络抓取,我有以下课程。
class ServiceManager {
static let sharedInstance = ServiceManager()
func fetchUsers(completion: @escaping ([User]?, Error?) -> ()) {
Alamofire.request(
URL(string: UrlManager.getUsers)!,
method: .get,
encoding: JSONEncoding.default
).responseJSON { (response) -> Void in
guard let responseCode = response.response?.statusCode else {
return
}
print(responseCode)
switch responseCode {
case 200:
do {
let users = try JSONDecoder().decode([User].self, from: response.data!)
completion(users, nil)
} catch let jsonErr {
print("ERR=", jsonErr.localizedDescription, "-->", jsonErr)
completion(nil, jsonErr)
}
default:
completion(nil, "error" as? Error)
}
}
}
}
方法1 如下。
class ViewController: UIViewController {
var userviewmodels = [UserViewModel]()
private var selectedIndex: Int!
@IBOutlet weak var tableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
tableView.delegate = self
tableView.dataSource = self
fetchUserData()
}
func fetchUserData() {
ServiceManager.sharedInstance.fetchUsers { (users, err) in
if let error = err {
print("Failed to fetch: \(error)")
return
}
self.userviewmodels = users?.map({return UserViewModel(user: $0)}) ?? []
self.tableView.reloadData()
}
}
}
extension ViewController: UITableViewDataSource, UITableViewDelegate {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return userviewmodels.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! UserTableViewCell
let userViewModel = userviewmodels[indexPath.row]
cell.userViewModel = userViewModel
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
selectedIndex = indexPath.row
performSegue(withIdentifier: "showUserInfo", sender: nil)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "showUserInfo" {
let userInfoViewController = segue.destination as! UserInfoViewController
userInfoViewController.userviewmodel = self.userviewmodels[selectedIndex]
}
}
}
视图模型如下:
class UserViewModel {
var name: String!
var email: String!
init(user: User) {
self.name = user.name
self.email = user.email
}
}
tableview单元格:
class UserTableViewCell: UITableViewCell {
@IBOutlet weak var labelName: UILabel!
@IBOutlet weak var labelEmail: UILabel!
var userViewModel: UserViewModel! {
didSet {
labelName.text = userViewModel.name
labelEmail.text = userViewModel.email
}
}
}
接下来,下面给出 APPROACH 2 。
class ViewController: UIViewController {
@IBOutlet weak var tableView: UITableView!
private var userviewmodel = UserViewModel()
private var selectedIndex: Int!
override func viewDidLoad() {
super.viewDidLoad()
tableView.delegate = self
tableView.dataSource = self
userviewmodel.delegate = self
userviewmodel.fetchUsers()
}
}
extension ViewController: UITableViewDataSource, UITableViewDelegate {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return userviewmodel.numberOfUsers()
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! UserTableViewCell
cell.labelName.text = userviewmodel.getUserName(at: indexPath.row)
cell.labelEmail.text = userviewmodel.getUserEmail(at: indexPath.row)
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
selectedIndex = indexPath.row
performSegue(withIdentifier: "showUserInfo", sender: nil)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "showUserInfo" {
let userInfoViewController = segue.destination as! UserInfoViewController
userInfoViewController.userviewmodel = userviewmodel
userInfoViewController.index = selectedIndex
}
}
}
extension ViewController: UserViewModelDelegate {
func usersFetched () {
tableView.reloadData()
}
}
对应的视图模型为:
protocol UserViewModelDelegate: class {
func usersFetched()
}
class UserViewModel {
weak var delegate: UserViewModelDelegate?
private var users = [User]() {
didSet {
delegate?.usersFetched()
}
}
}
extension UserViewModel {
func fetchUsers() {
ServiceManager.sharedInstance.fetchUsers { (users, err) in
if let error = err {
print("Failed to fetch: \(error)")
return
}
self.users = users!
}
}
}
extension UserViewModel {
func numberOfUsers() -> Int {
return users.count
}
func getUserName(at index: Int) -> String {
return users[index].name!
}
func getUserEmail(at index: Int) -> String {
return users[index].email!
}
}
据我了解,ViewModel应该将要呈现的数据通知给ViewController。那么这是否意味着网络调用应该放在ViewModel中(如方法2)?
并且,假设我需要在不同的ViewController中修改用户信息(上述示例中触发的选择)。在那种情况下,将ViewModel对象传递给另一个ViewController是否有效,尤其是对于方法2?
我已经读过MVVM,最好使用RxSwift和RxCocoa,但我仍在学习中。那么我现在应该使用哪种方法:方法1,方法2或其他?