Mat*_*der 19 viper ios viper-architecture
我有一个包含表视图的视图控制器,所以我想问一下我应该把表视图数据源和委托放在哪里,如果它是一个外部对象,或者如果我们说VIPER模式我可以在我的视图控制器中写它.
通常使用模式我这样做:
在viewDidLoad中,我从演示者请求一些流程 self.presenter.showSongs()
Presenter包含交互器,在showSongs方法中,我从交互器请求一些数据,如:self.interactor.loadSongs()
当歌曲准备好传回视图控制器时,我再次使用演示者来确定如何在视图控制器中显示这些数据.但我的问题是如何处理表视图的数据源?
Kon*_*tin 19
首先,您的View不应该询问Presenter的数据 - 它违反了VIPER架构.
视图是被动的.它等待Presenter给它显示内容; 它永远不会要求Presenter提供数据.
至于你的问题:最好在Presenter中保持当前的视图状态,包括所有数据.因为它基于州提供VIPER部分之间的通信.
但另一方面,Presenter不应该对UIKit有任何了解,因此UITableViewDataSource和UITableViewDelegate应该是View层的一部分.
为了使ViewController保持良好状态并以"SOLID"方式执行,最好将DataSource和Delegate保存在单独的文件中.但这些部分仍然应该知道主持人询问数据.所以我更喜欢在Extension of ViewController中做到这一点
所有模块应该看起来像这样:
视图
ViewController.h
extern NSString * const TableViewCellIdentifier;
@interface ViewController
@end
Run Code Online (Sandbox Code Playgroud)
ViewController.m
NSString * const TableViewCellIdentifier = @"CellIdentifier";
@implemntation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
[self.presenter setupView];
}
- (void)refreshSongs {
[self.tableView reloadData];
}
@end
Run Code Online (Sandbox Code Playgroud)
视图控制器+ TableViewDataSource.h
@interface ViewController (TableViewDataSource) <UITableViewDataSource>
@end
Run Code Online (Sandbox Code Playgroud)
视图控制器+ TableViewDataSource.m
@implementation ItemsListViewController (TableViewDataSource)
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return [self.presenter songsCount];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
Song *song = [self.presenter songAtIndex:[indexPath.row]];
// Configure cell
return cell;
}
@end
Run Code Online (Sandbox Code Playgroud)
视图控制器+ TableViewDelegate.h
@interface ViewController (TableViewDelegate) <UITableViewDelegate>
@end
Run Code Online (Sandbox Code Playgroud)
视图控制器+ TableViewDelegate.m
@implementation ItemsListViewController (TableViewDelegate)
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
Song *song = [self.presenter songAtIndex:[indexPath.row]];
[self.presenter didSelectItemAtIndex:indexPath.row];
}
@end
Run Code Online (Sandbox Code Playgroud)
主持人
Presenter.m
@interface Presenter()
@property(nonatomic,strong)NSArray *songs;
@end
@implementation Presenter
- (void)setupView {
[self.interactor getSongs];
}
- (NSUInteger)songsCount {
return [self.songs count];
}
- (Song *)songAtIndex:(NSInteger)index {
return self.songs[index];
}
- (void)didLoadSongs:(NSArray *)songs {
self.songs = songs;
[self.userInterface refreshSongs];
}
@end
Run Code Online (Sandbox Code Playgroud)
交互器
Interactor.m
@implementation Interactor
- (void)getSongs {
[self.service getSongsWithCompletionHandler:^(NSArray *songs) {
[self.presenter didLoadSongs:songs];
}];
}
@end
Run Code Online (Sandbox Code Playgroud)
Swift 3.1中的示例,可能对某些人有用:
视图
class SongListModuleView: UIViewController {
// MARK: - IBOutlets
@IBOutlet weak var tableView: UITableView!
// MARK: - Properties
var presenter: SongListModulePresenterProtocol?
// MARK: - Methods
override func awakeFromNib() {
super.awakeFromNib()
SongListModuleWireFrame.configure(self)
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
presenter?.viewWillAppear()
}
}
extension SongListModuleView: SongListModuleViewProtocol {
func reloadData() {
tableView.reloadData()
}
}
extension SongListModuleView: UITableViewDataSource {
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return presenter?.songsCount ?? 0
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let cell = tableView.dequeueReusableCell(withIdentifier: "SongCell", for: indexPath) as? SongCell, let song = presenter?.song(atIndex: indexPath) else {
return UITableViewCell()
}
cell.setupCell(withSong: song)
return cell
}
}
Run Code Online (Sandbox Code Playgroud)
主持人
class SongListModulePresenter {
weak var view: SongListModuleViewProtocol?
var interactor: SongListModuleInteractorInputProtocol?
var wireFrame: SongListModuleWireFrameProtocol?
var songs: [Song] = []
var songsCount: Int {
return songs.count
}
}
extension SongListModulePresenter: SongListModulePresenterProtocol {
func viewWillAppear() {
interactor?.getSongs()
}
func song(atIndex indexPath: IndexPath) -> Song? {
if songs.indices.contains(indexPath.row) {
return songs[indexPath.row]
} else {
return nil
}
}
}
extension SongListModulePresenter: SongListModuleInteractorOutputProtocol {
func reloadSongs(songs: [Song]) {
self.songs = songs
view?.reloadData()
}
}
Run Code Online (Sandbox Code Playgroud)
交互器
class SongListModuleInteractor {
weak var presenter: SongListModuleInteractorOutputProtocol?
var localDataManager: SongListModuleLocalDataManagerInputProtocol?
var songs: [Song] {
get {
return localDataManager?.getSongsFromRealm() ?? []
}
}
}
extension SongListModuleInteractor: SongListModuleInteractorInputProtocol {
func getSongs() {
presenter?.reloadSongs(songs: songs)
}
}
Run Code Online (Sandbox Code Playgroud)
线框
class SongListModuleWireFrame {}
extension SongListModuleWireFrame: SongListModuleWireFrameProtocol {
class func configure(_ view: SongListModuleViewProtocol) {
let presenter: SongListModulePresenterProtocol & SongListModuleInteractorOutputProtocol = SongListModulePresenter()
let interactor: SongListModuleInteractorInputProtocol = SongListModuleInteractor()
let localDataManager: SongListModuleLocalDataManagerInputProtocol = SongListModuleLocalDataManager()
let wireFrame: SongListModuleWireFrameProtocol = SongListModuleWireFrame()
view.presenter = presenter
presenter.view = view
presenter.wireFrame = wireFrame
presenter.interactor = interactor
interactor.presenter = presenter
interactor.localDataManager = localDataManager
}
}
Run Code Online (Sandbox Code Playgroud)
1)首先,View是passive不应该为Presenter询问数据的。因此,替换self.presenter.showSongs()为self.presenter.onViewDidLoad()。
2)在您的Presenter上,在实现时,onViewDidLoad()通常应调用交互程序以获取一些数据。然后,交互者将呼叫例如self.presenter.onSongsDataFetched()
3)在Presenter上,在实现onSongsDataFetched()时应按照视图所需的格式准备数据,然后调用self.view.showSongs(listOfSongs)
4)在您的View上,在的实现上showSongs(listOfSongs),应先设置self.mySongs = listOfSongs然后调用tableView.reloadData()
5)您的TableViewDataSource将在您的数组上运行mySongs并填充TableView。
有关VIPER架构的更多高级技巧和有用的良好实践,我建议发布此帖子:https : //www.ckl.io/blog/best-practices-viper-architecture(包括示例项目)
| 归档时间: |
|
| 查看次数: |
6273 次 |
| 最近记录: |