[TIL] SceneDelegate UI 초기화 로직 리팩토링

Eden·2025년 2월 3일
0

TIL

목록 보기
112/129
post-thumbnail

상황

기존 코드에서는 Task를 사용하여 비동기적으로 AuthorizationCenter.shared.authorizationStatus를 가져온 후,

비동기적으로 rootViewController를 설정하고 DispatchQueue.main.async에서 UI를 업데이트했다.

하지만 scene(_:willConnectTo:options:) 메서드는 메인 스레드에서 실행돼서,

UI 업데이트를 완전히 안전하게 수행하기 위해 DispatchQueue.main.async 대신 await MainActor.run을 사용하도록 개선했다.


기존 코드

...

var rootViewController: UIViewController?

	Task {
    	let authorizationsStatus = AuthorizationCenter.shared.authorizationStatus

      // 권한 승인 여부 확인
      if authorizationsStatus == .approved {
      	  // 권한 설정 되어있을 때, 메인페이지로
          rootViewController = MainViewController()
      } else {
          // 권한 설정 안되어있을 때, 권한 설정 페이지로
          rootViewController = PermissionViewController()
      }

      DispatchQueue.main.async {
          guard let rootViewController = rootViewController else { return }
          navigationController.setViewControllers([rootViewController], animated: true)
      }
  }

🚨 문제점

  • Task 내부에서 rootViewController를 설정했기 때문에 비동기 실행으로 인해 nil 상태에서 UI 업데이트가 실행될 가능성이 있음.
  • DispatchQueue.main.async를 사용했지만, scene(_:willConnectTo:options:) 메서드는 원래 메인 스레드에서 실행되므로 불필요하다고 생각이 들어서 제외했더니 동작을 안함.
  • rootViewController가 옵셔널(var rootViewController: UIViewController?)로 선언되어 옵셔널 체크가 필요해짐.

개선된 코드

func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
    guard let scene = (scene as? UIWindowScene) else { return }

    let window = UIWindow(windowScene: scene)
    let navigationController = UINavigationController()
    self.window = window
    window.rootViewController = navigationController
    window.makeKeyAndVisible()

    Task {
        let authorizationsStatus = AuthorizationCenter.shared.authorizationStatus
        let rootViewController: UIViewController
		
        // 권한 승인 여부 확인
        if authorizationsStatus == .approved {
        	// 권한 설정 되어있을 때, 메인페이지로
            rootViewController = MainViewController()
        } else {
        	// 권한 설정 안되어있을 때, 권한 설정 페이지로
            rootViewController = PermissionViewController()
        }

        await MainActor.run {
            navigationController.setViewControllers([rootViewController], animated: true)
        }
    }
}

✅ 개선된 점

  1. DispatchQueue.main.async → await MainActor.run 사용
    • DispatchQueue.main.async 대신 await MainActor.run을 사용하여 UI 업데이트를 보다 안전하게 실행.
    • Swift Concurrency 시스템이 UI 업데이트를 항상 메인 스레드에서 실행하도록 보장.
  2. 비동기 코드의 안정성 향상
    • Task 내부에서 rootViewControllerlet으로 선언하여 옵셔널 체크 없이도 안정적으로 값이 설정되도록 보장.
    • Task 내부 코드가 순차적으로 실행됨을 명확히 하기 위해 await을 사용.
  3. 코드 가독성 및 유지보수성 개선
    • navigationController.setViewControllers를 옵셔널 체크 없이 바로 실행 가능.
    • UI 업데이트 관련 로직이 더 명확해짐.

배운 점

  • SceneDelegate의 scene(_:willConnectTo:options:)는 기본적으로 메인 스레드에서 실행되지만, Task 내부의 코드는 비동기로 실행될 수 있다.
  • UI 업데이트는 메인 스레드에서 실행해야 하므로, DispatchQueue.main.async보다 await MainActor.run이 더 적절하다.
  • 비동기 코드에서도 옵셔널 사용을 최소화하여 코드의 안정성을 높이는 것이 중요하다.
profile
Frontend🌐 and iOS

0개의 댓글

관련 채용 정보