iOS) Coordinator pattern 적용해보기 - Basic
참고
- 간단한 예제로 살펴보는 iOS Design/Architecture Pattern: Coordinator - Basic
- [Swift] Coordinator Pattern (1/2) - 기본원리
위의 글을 참고해서 coordinator pattern 을 실습해보았다.
초기설정
👊 SceneDelegate.swift 삭제
- App Delegate에서 Scene delegate 메서드 삭제
- Scene delegate file 삭제
- Info.plist에서 UIApplicationSceneManifest 삭제
- AppDelegate에
var window:UIWindow?
추가
- Scene delegate가 추가된 iOS 13이후에는 위 과정을 거치지 않으면 Coordinator를 이용한 화면전환이 정상적으로 동작하지 않습니다.
👊 첫 화면을 실행해 보자
- Coordinator.swift
import Foundation
import UIKit
protocol Coordinator {
var childCoordinators: [Coordinator] { get set }
var navigationController: UINavigationController { get set }
func start()
}
- MainCoordinator.swfit
Coordinator 프로토콜을 채택해서 화면전환을 설정
import Foundation
import UIKit
class MainCoordinator: NSObject, Coordinator {
var childCoordinators = [Coordinator]()
var nav: UINavigationController
init(navigationController: UINavigationController) {
self.nav = navigationController
}
// ✅ 앱이 보여질 때 처음 화면
func start() {
// ✅ 뷰컨트롤러 인스턴스 커스텀 메서드
let vc = ViewController.instantiate()
vc.coordinator = self
nav.pushViewController(vc, animated: false)
}
}
- Storyboarded.swift
스토리보드에 접근해서 뷰컨트롤러의 이름을 identifier 로 가진 뷰컨트롤러를 손쉽게 인스턴스화하기 위한 instantiate()
메서드 구현.
import Foundation
import UIKit
protocol Storyboarded {
static func instantiate() -> Self
}
extension Storyboarded where Self: UIViewController {
static func instantiate() -> Self {
// ✅ ex) "CoordinatorPractice(프로젝트 이름).ViewController"
let fullName = NSStringFromClass(self)
// ✅ .을 기준으로 split해 "ViewController"만 추출
let className = fullName.components(separatedBy: ".")[1]
// ✅ Bundle.main에서 Main.storyboard 가져오기
let storyboard = UIStoryboard(name: "Main", bundle: Bundle.main)
// ✅storyboard에 className을 identifier로 가지고 있는 ViewController 인스턴스화
return storyboard.instantiateViewController(withIdentifier: className) as! Self
}
}
다음과 같이 id 값을 설정해주어야 한다.
- ViewController.swift
import UIKit
class ViewController: UIViewController, Storyboarded {
// ✅ MainCoorinator 추가
weak var coordinator: MainCoordinator?
override func viewDidLoad() {
super.viewDidLoad()
}
}
- AppDelegate.swift
SeneDelegate.swift 를 삭제한 후 첫 화면을 실행하기 위한 코드
import UIKit
@main
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
var coordinator: MainCoordinator?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
let navigationContorller = UINavigationController()
coordinator = MainCoordinator(navigationController: navigationContorller)
coordinator?.start()
window = UIWindow(frame: UIScreen.main.bounds)
window?.rootViewController = navigationContorller
// ✅
window?.makeKeyAndVisible()
return true
}
}
- window 보여주기 및 keyWindow 설정 메서드.
- keyWindow: 키보드 및 터치 이벤트가 아닌 이벤트도 받을 수 있도록 등록
- window의 rootViewController를 위에서 세팅해주고 makeKeyAndVisible() 부르면 마침내 지정한 rootViewController가 상호작용을 받는 현재 화면으로 세팅 완료
참고 :
[iOS - swift] UIWindow, makeKeyAndVisible()
👊 화면 전환을 해보자
- Main.storyboard
추가로 LeftViewController 와 RightViewController 를 만들어주고 ViewController 와 동일하게 weak var coordinator: MainCoordinator?
추가하고 Storyboarded
프로토콜을 채택해준다.
- MainCoordinator.swift
class MainCoordinator: NSObject, Coordinator {
// ...
// ✅ 추가 화면 전환
func pushToLeftVC() {
let vc = LeftViewController.instantiate()
vc.coordinator = self
// ✅ push 되는 애니메이션 여부 true 설정
nav.pushViewController(vc, animated: true)
}
func pushToRightVC() {
let vc = RightViewController.instantiate()
vc.coordinator = self
nav.pushViewController(vc, animated: true)
}
}
👊 데이터 전달
그렇다면 coordinator 패턴에서는 어떻게 화면전환 시 데이터를 전달해줄까?
비슷하다. MainCoordinator 의 화면전환 함수에 파라미터를 만들어준다. 그 후 뷰컨트롤러에서 호출할 때 파라미터를 전달받아서 화면전환할 뷰컨트롤러의 인스턴스에 넣어주면 된다.
- MainCoordinator.swift
class MainCoordinator: NSObject, Coordinator {
// ...
func pushToLeftVC(string: String) {
let vc = LeftViewController.instantiate()
vc.coordinator = self
// ✅ 데이터 전달
vc.string = string
nav.pushViewController(vc, animated: true)
}
// ...
}
- LeftViewController.swift
import UIKit
class LeftViewController: UIViewController, Storyboarded {
// ✅ 데이터를 받을 옵셔널 변수
var string: String?
weak var coordinator: MainCoordinator?
override func viewDidLoad() {
super.viewDidLoad()
// ✅ 전달된 데이터 확인
if let string = string {
print(string)
}
}
}