iyOmSd/Title: Swift

[Swift] Coordinator Pattern (2/2) - 기본동작

냄수 2020. 5. 22. 11:19
반응형

2020/05/17 - [iyOmSd/Title: Swift] - [Swift] Coordinator Pattern (1/2) - 기본원리

 

[Swift] Coordinator Pattern (1/2) - 기본원리

iOS 아키텍쳐의 종류는 다양해요 MVC, MVP, MVVM, VIPER, RIBs... 등등 많은데 MVC-C, MVVM-C 이러한 표현을 보신적도 있지않나요?? 여기서 C는 Coordinator를 의미해요 Coordinator란?? 화면의 흐름을 제어해주는..

nsios.tistory.com

 

전게시글에서 Coordinator가 어떤 원리로 돌아가는지 느낌을 살펴봤구요

이제 써먹을 수 있도록(?) 구현해볼게요

 

 

제가 생각한 Coordinator는 Navigation을 각 하나씩 가지고있어요

따라서 어떤뷰를 present시킨다면 Navigation을 present하게 할거에요

(= Coordinator를 present)

왜냐하면 해당 Coordinator에서도 push이벤트가 발생할경우

NavigationController가 없다면  동작하지 않는다고 생각했기 때문이에요

 

 

그렇게하나면 Coordinator가 띄워질때 자신의 부모Coordinator도 알고 있어야겠죠?

이유는 아래에서 다시 언급할게요

 

 

이를토대로

Coordinator프로토콜을 한번 정의해볼께요

 

protocol Coordinator {
    var parentCoordinateor: Coordinator? { get set }
    var childCoordinators: [Coordinator] { get set }
    var navigation: UINavigationController { get set }
    
    func storyboardStart()
}

 

 

Class를 구현을 해볼까요..?

class MainCoordinator: Coordinator {
    var parentCoordinateor: Coordinator?
    var childCoordinators: [Coordinator] = []
    var navigation: UINavigationController
    
    init(parent: Coordinator?, navigation: UINavigationController) {
        self.navigation = navigation
        self.parentCoordinateor = parent
    
        storyboardStart()
    }

    func storyboardStart() {
        DispatchQueue.main.async {
            let vc = ViewController()
            vc.view.backgroundColor = .purple
            vc.coordinator = self
            self.navigation.pushViewController(vc, animated: false)
        }
    }
}

생성될 때

Navigation을 받아오고

부모Coordinator를 받아온뒤

스토리보드 혹은 코드로 기본화면이 될 VC를 띄워줬어요

 

 

 

여기서이제 추가적으로 뷰를 이동시키는 작업을 해야겠죠?

이벤트를 받을 Protocol을 구현하고 delegate패턴을 적용할거에요

 

protocol ViewControllerHandler: AnyObject {
    func click(event: Event)
}

enum EventType {
    case push
    case pop
    case present
    case dismiss
}

 

click함수에 Event타입을 넣어주고

그 타입을 분기처리해서 전환을 처리를 할거에요

 

 

 

 

Coordinator에 프로토콜을 적용시키고

VC에 delegate를 넣어줄게요

 

 

class ViewController: UIViewController, Storyboarded {
    
    weak var coordinator: MainCoordinator?
    var delegate: ViewControllerHandler?
    
    func movePush() {
        delegate.click(event: .push)
    }
}

 

 

이런식으로 push이벤트를 넘겨주면

ViewControllerHandler를 채택한 곳에서 이벤트가 발생하겟죠?

Coordinator가 뷰컨의 이벤트를 받아서 어느 화면으로 갈지 결정하도록 하면되요

아래는 간단하게 화면전환만 다뤘고 이런식으로 넘기는 로직을 구현하면되요

 

class MainCoordinator: Coordinator {
    var parentCoordinateor: Coordinator?
    var childCoordinators: [Coordinator] = []
    var navigation: UINavigationController
    
    init(parent: Coordinator?, navigation: UINavigationController) {
        self.navigation = navigation
        self.parentCoordinateor = parent
    
        storyboardStart()
    }

    func storyboardStart() {
        DispatchQueue.main.async {
            let vc = ViewController()
            vc.view.backgroundColor = .purple
            vc.coordinator = self
            vc.delegate = self
            self.navigation.pushViewController(vc, animated: false)
        }
    }
}

extension MainCoordinator: ViewControllerHandler {
    func click(event: Event) {
        DispatchQueue.main.async {
            switch event {
            
            case .push:
                let nextVC = ViewController()
                nextVC.coordinator = self
                nextVC.delegate = self
                self.navigation.pushViewController(nextVC, animated: true)
            case .present:
                let navi = UINavigationController()
                navi.modalPresentationStyle = .fullScreen
                let childCoordinator = MainCoordinator(parent: self, navigation: navi)
                self.childCoordinators.append(childCoordinator)
                self.navigation.present(navi, animated: true)
            case .pop:
                self.navigation.popViewController(animated: true)
            case .dismiss:
                self.parentCoordinateor?.childCoordinators.removeLast()
                self.navigation.dismiss(animated: true)
            }
        }
}

 

 

더 복잡한 로직을 구현하다보면

delegate 이벤트를 발생시키는데 동작을 안하는경우가 종종 있어요

그럴때는 아마도 delegate = self를 하면서 전달받다가

화면이 사라질경우 delegate또한 nil이 되기때문에 작동안하는 경우가 있더라구요

Class타입이기때문에 메모리관리도 신경쓰면서 구현해야할 필요가 있어요

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

반응형