뷰 컨트롤러 간 데이터 전달해보기

Wonbi·2022년 9월 8일

⛏ 삽질 내용

💎 뷰 컨트롤러간 데이터를 전달하기 위한 삽질과정..

  • 처음 뷰 컨트롤러 사이에서 데이터를 주고받는 것은 생각보다 간단했다.
  • 그냥 로드할 뷰를 인스턴스화 시키고 거기다가 데이터 주입해서 그걸 화면에 띄우면 그만이었다.
//MARK: - 메인 뷰 소스 파일
class MainViewController: UIViewController {
    private let store = FruitStore(stockCount: 10)
    private lazy var juiceMaker = JuiceMaker(store: store)
    
    @IBAction private func tappedModifyBarButton(_ sender: UIBarButtonItem) {
        guard let navigationController = self.storyboard?.instantiateViewController(withIdentifier: "EditNavigationController") as? UINavigationController else { return }
        guard let viewController = navigationController.viewControllers.first as? EditViewController else { return }
        navigationController.modalTransitionStyle = UIModalTransitionStyle.flipHorizontal
        
        viewController.stock = store.stock
        
        present(navigationController, animated: true)
    }
}

//MARK: - 재고수정 소스 파일
class EditViewController: UIViewController {
    @IBOutlet weak var strawberryCount: UILabel!
    @IBOutlet weak var bananaCount: UILabel!
    @IBOutlet weak var pineappleCount: UILabel!
    @IBOutlet weak var kiwiCount: UILabel!
    @IBOutlet weak var mangoCount: UILabel!
    
    var stock: [Int]?
    
    override func viewDidLoad() {
        super.viewDidLoad()
        if let stock = stock {
            strawberryCount.text = String(stock[Fruit.strawberry.index])
            bananaCount.text = String(stock[Fruit.banana.index])
            pineappleCount.text = String(stock[Fruit.pineapple.index])
            kiwiCount.text = String(stock[Fruit.kiwi.index])
            mangoCount.text = String(stock[Fruit.mango.index])
        }
    }
}
  • 가장 쉽고 직관적인 방법이었다. 하지만 이는 컨트롤러간 결합도를 올려 나중에 유지보수가 힘들고 강한 결합으로 인한 문제들이 튀어나오게 될 것이다.
  • 여기서 나의 삽질이 시작되었다.

✏️ 1. Delegate 사용

//MARK: - 메인 뷰 소스 파일
protocol SendDataDelegate: AnyObject {
    func sendStock(_ stock: [Int])
}

class MainViewController: UIViewController {
    weak var delegate: SendDataDelegate?
    
    @IBAction private func tappedModifyBarButton(_ sender: Any) {
        delegate?.sendStock(store.stock)
        present(navigationController, animated: true)
    }
}

//MARK: - 재고수정 소스 파일
extension EditViewController: SendDataDelegate {
    func sendStock(_ stock: [Int]) {
        updateStockCount(stock: stock)
    }
}

class EditViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        guard let mainViewController = self.storyboard?.instantiateViewController(withIdentifier: "MainViewController") as? MainViewController else { return }
        
        mainViewController.delegate = self
    }
}
  • 결과먼저 말하면, 이 방법으로 사용하는 것은 실패했다.. 정확한 이유를 열심히 찾아보았지만 찾을 수는 없었고, 재고 수정 소스파일의 뷰컨트롤러 안에서 새로운 메인 뷰 컨트롤러를 인스턴스화 하고 위임자를 지정하는데, 그 위임자가 현재 실행중인 메인 뷰가 아니라 새로 만든 인스턴스이기 때문이라고 생각하였다.

  • 이 반대의 상황이라면(재고 수정에서 메인으로 데이터를 전달하고자 할 때), 모달 창이 dismiss되면서 메모리상에서 사라졌다가, 다시 창을 부르면 새롭게 인스턴스화 시켜서 present하기 때문에 구현이 가능할 거 같다고 생각했다. 나중에 함 해봐야지

  • 분명 여기서 위임자를 통해 재고수정 뷰에 값을 넘겨주는 방법이 있을 것 같은데.. 지금은 모르겠다.

✏️ 2. appDelegate 사용

  • 무튼, 이 다음 생각한 방식은 appDelegate를 활용하는 것이었다. appDelegate안에 stock을 선언하고 장면 전환시 여기에 데이터를 전달한 후, 뷰가 새로 올라올 때 이 데이터를 취하는 방식이었다.
@main
class AppDelegate: UIResponder, UIApplicationDelegate {

    var stock: [Int]?
    
}
//MARK: - 메인 뷰 소스 파일
class MainViewController: UIViewController {    
    @IBAction private func tappedModifyBarButton(_ sender: UIBarButtonItem) {
        guard let navigationController = self.storyboard?.instantiateViewController(withIdentifier: "EditNavigationController") as? UINavigationController else { return }
        navigationController.modalTransitionStyle = UIModalTransitionStyle.flipHorizontal

        guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else { return }
        appDelegate.stock = store.stock
        
        present(navigationController, animated: true)
    }
}

//MARK: - 재고수정 소스 파일
class EditViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        guard let delegate = UIApplication.shared.delegate as? AppDelegate,
              let delegateStock = delegate.stock else { return }
        updateStockCount(stock: delegateStock)
        delegate.stock = nil
    }
  • 이 방법을 활용하니 하나의 저장소를 만들어서 그 저장소를 통해 데이터를 주고받기 때문에, 결합도를 낮출 수 있다고 판단하였다.

  • 하지만, app delegate라는 것에 대해 더 찾아본 결과, 이 app delegate는 앱의 상태에 따라 응답하는 콘텐츠가 그려지는 창(window)을 만드는 역할이고, entry point와, 앱의 입력 이벤트를 전달하는 run loop를 생성하는 역할이라고 하는데.. 아니 도대체 이게 무슨말이야

✏️ 3. Singleton 패턴 사용하기

  • 위에 시도했던 AppDelegate가 마치 싱글톤 처럼 사용되는 느낌을 받아서, 차라리 스토리지용 싱글톤을 만들어보자! 라고 생각하게 되었다.

//MARK: - 재고스토리지 소스 파일
class StockStorage {
    static let shared: StockStorage = StockStorage()
     var stock: [Int]?
    
    private init() {}
}

//MARK: - 메인 뷰 소스 파일
class MainViewController: UIViewController {
    @IBAction private func tappedModifyBarButton(_ sender: Any) {
        guard let navigationController = self.storyboard?.instantiateViewController(withIdentifier: "EditNavigationController") as? UINavigationController else { return }
        navigationController.modalTransitionStyle = UIModalTransitionStyle.coverVertical
        navigationController.modalPresentationStyle = UIModalPresentationStyle.fullScreen
        
        StockStorage.shared.stock = store.stock
        
        present(navigationController, animated: true)
    }
}

   //MARK: - 재고수정 소스 파일 
class EditViewController: UIViewController {
    
    @IBOutlet var fruitCountLabelArray: [UILabel]!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        guard let storageStock = StockStorage.shared.stock else { return }
        
        updateStockCount(stock: storageStock)
        StockStorage.shared.stock = nil
    }
}
  • 이러한 용도로 싱글톤을 사용하는 것이 적절한지 약간의 의문이 있었지만, 기존에 사용하던 방법보다는 더 나은 방법이라 판단하였다.

  • 어떤 방법이 더 좋은 방법이고, 아직 모르는 부분도 많지만 앞으로 배우고 공부하다 보면 완벽히 이해하고 더 합리적이고 논리적인 이유를 가지고 코드를 활용하게 되겠지..

0개의 댓글