간단 한 사용자 목록 인터페이스 로 보기: iOS 에서 MVP 사용 하기 (번역)
iOS 응용 프로그램 을 개발 할 때 Model - View - Controller 는 흔히 볼 수 있 는 디자인 모델 이다.일반적으로 View 층 은 UIKit 의 요소 로 구성 되 는데 이러한 요 소 는 코드 정의 나 xib 파일 로 정의 되 고 Model 층 은 응용 프로그램의 업무 논 리 를 포함 하 며 UIViewController 류 가 표시 하 는 Controller 층 은 Model 과 View 간 의 접착제 이다.
이 모델 의 좋 은 부분 은 업무 논리 와 업무 규칙 을 Model 층 에 밀봉 하 는 것 이다.그러나 UIViewController 는 UI 와 관련 된 논 리 를 포함 하고 있 습 니 다.
그래서 이 제 는 MVC 를 개선 해 이런 문 제 를 처리 할 때 가 됐다.Model - View - Presenter MVP 의 개선 이 라 고 합 니 다.
MVP 모델 은 1996 년 마이크 포 텔 이 처음 도 입 했 으 며 수년 간 여러 차례 논 의 된 바 있다.그의 글 GUI 구조 에서 Martin Fowler 는 이러한 모델 에 대해 논 의 했 고 UI 코드 를 관리 하 는 다른 모델 과 비교 했다.MVP 는 많은 변체 가 있 는데, 그들 사이 의 차 이 는 매우 작다.이 글 에서 나 는 현재 응용 프로그램 개발 에 자주 사용 되 는 상용 프로그램 을 선택 했다.이 변체 의 특징 은:
다음 예제 에 서 는 작업 중 MVP 를 사용 하 는 방법 을 보 여 줍 니 다.
우리 의 예 는 매우 간단 한 프로그램 으로 간단 한 사용자 목록 을 표시 합 니 다.Github 에서 완전한 소스 코드 를 얻 을 수 있 습 니 다: https: / github. com / iyadagha / iOS - mvp - sample.(Swift + OC 두 버 전 은 문장 끝 에 예 시 됩 니 다)
사용자 정보의 간단 한 데이터 모델 부터 시작 합 니 다.
struct User {
let firstName: String
let lastName: String
let email: String
let age: Int
}
그리고 저 희 는 간단 한 UserService 를 실현 합 니 다. 사용자 목록 을 다른 단계 로 되 돌려 줍 니 다.
class UserService {
//the service delivers mocked data with a delay
func getUsers(callBack:([User]) -> Void){
let users = [User(firstName: "Iyad", lastName: "Agha", email: "[email protected]", age: 36),
User(firstName: "Mila", lastName: "Haward", email: "[email protected]", age: 24),
User(firstName: "Mark", lastName: "Astun", email: "[email protected]", age: 39)
]
let delayTime = dispatch_time(DISPATCH_TIME_NOW, Int64(2 * Double(NSEC_PER_SEC)))
dispatch_after(delayTime, dispatch_get_main_queue()) {
callBack(users)
}
}
}
다음 단 계 는 User Presenter 를 작성 하 는 것 입 니 다.우선 사용자 의 데이터 모델 이 필요 합 니 다. 보기 에서 직접 사용 할 수 있 습 니 다.필요 에 따라 보기 에서 정확하게 포맷 된 데 이 터 를 포함 합 니 다:
struct UserViewData{
let name: String
let age: String
}
그 다음 에 우 리 는 보 기 를 추상 적 으로 해 야 한다. 이것 은 presenter 가 UIViewController 를 모 르 는 상황 에서 사용 할 수 있다.우 리 는 프로 토 콜 UserView 를 정의 함으로써 이 점 을 할 수 있다.
protocol UserView: NSObjectProtocol {
func startLoading()
func finishLoading()
func setUsers(users: [UserViewData])
func setEmptyUsers()
}
이 프로 토 콜 은 presenter 에서 사용 되 며, 잠시 후 UIViewController 에서 실 현 됩 니 다.기본적으로 프로 토 콜 은 presenter 에서 보 기 를 제어 하 는 함수 에 포함 되 어 있 습 니 다.
사용자 자체 가 다음 과 같 습 니 다:
class UserPresenter {
private let userService:UserService
weak private var userView : UserView?
init(userService:UserService){
self.userService = userService
}
func attachView(view:UserView){
userView = view
}
func detachView() {
userView = nil
}
func getUsers(){
self.userView?.startLoading()
userService.getUsers{ [weak self] users in
self?.userView?.finishLoading()
if(users.count == 0){
self?.userView?.setEmptyUsers()
}else{
let mappedUsers = users.map{
return UserViewData(name: "\($0.firstName) \($0.lastName)", age: "\($0.age) years")
}
self?.userView?.setUsers(mappedUsers)
}
}
}
}
루트 는 함수 attachView (view: UserView) 와 attachView (view: UserView) 를 UIViewContoller 생명주기 방법의 더 많은 제어 에 사용 합 니 다. 우 리 는 뒤에서 볼 수 있 습 니 다.사용 자 를 UserView Data 로 전환 하 는 것 은 presenter 의 책임 입 니 다.또한 userView 는 보존 주 기 를 피하 기 위해 약 해 야 합 니 다.
실현 의 마지막 부분 은 UserView Controller 입 니 다.
class UserViewController: UIViewController {
@IBOutlet weak var emptyView: UIView?
@IBOutlet weak var tableView: UITableView?
@IBOutlet weak var activityIndicator: UIActivityIndicatorView?
private let userPresenter = UserPresenter(userService: UserService())
private var usersToDisplay = [UserViewData]()
override func viewDidLoad() {
super.viewDidLoad()
tableView?.dataSource = self
activityIndicator?.hidesWhenStopped = true
userPresenter.attachView(self)
userPresenter.getUsers()
}
}
사용자 목록 을 표시 하 는 tableView 가 있 습 니 다. empty View 는 사용자 가 사용 할 수 없 으 면 activity Indicator 가 프로그램 에서 사용 자 를 불 러 올 때 표 시 됩 니 다.이 밖 에 userPresenter 와 사용자 목록 도 있 습 니 다.
viewdLoad 방법 에서 UserViewController 는 자신 을 presenter 에 추가 합 니 다.이것 은 가능 하 다. 왜냐하면 우 리 는 곧 UserView Controller 가 UserView 협 의 를 실현 하 는 것 을 볼 수 있 기 때문이다.
extension UserViewController: UserView {
func startLoading() {
activityIndicator?.startAnimating()
}
func finishLoading() {
activityIndicator?.stopAnimating()
}
func setUsers(users: [UserViewData]) {
usersToDisplay = users
tableView?.hidden = false
emptyView?.hidden = true;
tableView?.reloadData()
}
func setEmptyUsers() {
tableView?.hidden = true
emptyView?.hidden = false;
}
}
우리 가 본 바 와 같이 이 기능 들 은 복잡 한 논 리 를 포함 하지 않 고 순 보기 관 리 를 하고 있 을 뿐이다.
마지막 으로 UITableView DataSource 의 실현 은 매우 기본 적 이 며 다음 과 같다.
extension UserViewController: UITableViewDataSource {
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return usersToDisplay.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = UITableViewCell(style: UITableViewCellStyle.Subtitle, reuseIdentifier: "UserCell")
let userViewData = usersToDisplay[indexPath.row]
cell.textLabel?.text = userViewData.name
cell.detailTextLabel?.text = userViewData.age
cell.textLabel
return cell
}
}
유닛 테스트
MVP 를 하 는 장점 중 하 나 는 UI 논리의 가장 큰 부분 을 테스트 할 수 있 고 UIViewController 자 체 를 테스트 할 필요 가 없다 는 것 이다.따라서 우리 presenter 가 좋 은 유닛 테스트 보급률 을 가지 고 있다 면 UIViewController 를 위해 유닛 테스트 를 작성 할 필요 가 없습니다.
이제 User Presenter 를 어떻게 테스트 하 는 지 살 펴 보 자.우선 우 리 는 두 개의 시 뮬 레이 션 작업 을 정의 한다.하나의 시 뮬 레이 션 은 UserService 가 필요 한 사용자 목록 을 제공 하도록 하 는 것 이다.또 다른 시 뮬 레이 션 은 UserView 로 방법 이 올 바 르 게 호출 되 었 는 지 검증 하 는 것 이다.
class UserServiceMock: UserService {
private let users: [User]
init(users: [User]) {
self.users = users
}
override func getUsers(callBack: ([User]) -> Void) {
callBack(users)
}
}
class UserViewMock : NSObject, UserView{
var setUsersCalled = false
var setEmptyUsersCalled = false
func setUsers(users: [UserViewData]) {
setUsersCalled = true
}
func setEmptyUsers() {
setEmptyUsersCalled = true
}
}
현재 Google 은 서비스 가 비어 있 지 않 은 사용자 목록 을 제공 할 때 presenter 의 행동 이 올 바른 지 테스트 할 수 있 습 니 다.
class UserPresenterTest: XCTestCase {
let emptyUsersServiceMock = UserServiceMock(users:[User]())
let towUsersServiceMock = UserServiceMock(users:[User(firstName: "firstname1", lastName: "lastname1", email: "[email protected]", age: 30),
User(firstName: "firstname2", lastName: "lastname2", email: "[email protected]", age: 24)])
func testShouldSetUsers() {
//given
let userViewMock = UserViewMock()
let userPresenterUnderTest = UserPresenter(userService: towUsersServiceMock)
userPresenterUnderTest.attachView(userViewMock)
//when
userPresenterUnderTest.getUsers()
//verify
XCTAssertTrue(userViewMock.setUsersCalled)
}
}
마찬가지 로 서비스 가 빈 사용자 목록 으로 돌아 가면 presenter 가 정상적으로 작 동 하 는 지 테스트 할 수 있 습 니 다.
func testShouldSetEmptyIfNoUserAvailable() {
//given
let userViewMock = UserViewMock()
let userPresenterUnderTest = UserPresenter(userService: emptyUsersServiceMock)
userPresenterUnderTest.attachView(userViewMock)
//when
userPresenterUnderTest.getUsers()
//verify
XCTAssertTrue(userViewMock.setEmptyUsersCalled)
}
변천 과정.
우 리 는 MVP 가 MVC 의 변천 이라는 것 을 이미 보 았 다.UI 논 리 를 Presenter 라 는 추가 구성 요소 에 넣 고 UIViewController passiv (Dump) 를 가능 하 게 해 야 합 니 다.
MVP 의 특징 중 하 나 는 presenter 와 View 가 서로 통신 하 는 것 이다.이 보기 (이 예 에서 UIViewController) 는 presenter 의 인용 을 제공 합 니 다. 반대로 도 마찬가지 입 니 다.응답 식 프로 그래 밍 을 사용 하여 presenter 에서 사용 하 는 보기 의 참 고 를 삭제 할 수 있 지만.ReactiveCocoa 나 Rx Swift 등 응답 식 프레임 워 크 를 사용 하면 하나의 시스템 구 조 를 구축 할 수 있 는데 그 중에서 보기 만 presenter 를 알 고 반대로 도 마찬가지 입 니 다.이런 상황 에서 이 구 조 는 MVVM 이 라 고 불 릴 것 이다.
? Contributions
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
다양한 언어의 JSONJSON은 Javascript 표기법을 사용하여 데이터 구조를 레이아웃하는 데이터 형식입니다. 그러나 Javascript가 코드에서 이러한 구조를 나타낼 수 있는 유일한 언어는 아닙니다. 저는 일반적으로 '객체'{}...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.