본문 바로가기

iOS/UIKit

UIKit - GCD + Alert 에러 (Call must be made on main thread, Unsupported enumeration of UIWindowScene windows on non-main thread.)

📗 Call must be made on main thread 에러

  • 현재 작성된 코드
guard (200..<300).contains(statusCode) else {
	var title: String
	var message: String

	switch statusCode {
	case 401..<500:
		title = "클라이언트 요청 에러"
		message = "클라이언트에서의 네트워크 연결 혹은 입력값이 잘못되어 데이터를 받아오지 못하였습니다"

	case 501..<600:
		title = "서버 에러"
		message = "서버의 문제가 생겨 데이터를 받아오지 못하였습니다"

	default:
		title = "알 수 없는 에러"
		message = "알 수 없는 에러로 데이터를 받아오지 못하였습니다"
	}

	// 1. alert 창 구성
	let alert = UIAlertController(title: title,
								  message: message,
								  preferredStyle: .alert)
	// 2. alert button 구성
	let check = UIAlertAction(title: "확인", style: .default)
	// 3. alert에 button 추가
	alert.addAction(check)

	DispatchQueue.main.async { [weak self] in
		guard let self else { return }
		present(alert, animated: true)
	}
	return
}

 

  • URLSession의 dataTask 메서드를 통해 받아온 response 값의 status code 를 확인하여 에러를 가지고 있는 status code를 만나면 Alert를 뷰에 띄어준다.
  • 처음에는 기본적인 present 메서드를 호출하였고
present(viewControllerToPresent: UIViewController, animated: Bool)

 

  • 이 상태로 앱을 빌드한 결과
  • 아래 스크린샷으로 보여지는 에러를 만나게 됐다.

 

  • 그래서 global thread에서 돌렸던 상황에서 아래 코드를 통해 main thread로 다시 thread를 main thread로 돌려놓았고 다시 빌드해 본 결과
DispatchQueue.main.async { [weak self] in
    guard let self else { return }
    present(alert, animated: true)
}

 

  • 화면에 Alert이 잘 보여지는 것을 볼 수 있다.

 

📘 Unsupported enumeration of UIWindowScene windows on non-main thread. 경고문

 

 

하지만 처음부터 계속 보여지고 있었지만 내가 보지 못하고 있던 경고문이 있었는데

Unsupported enumeration of UIWindowScene windows on non-main thread. 라는 경고문이 계속 Xcode 상에 출력이 되고 있었다.

 

의미부터 이해가 잘 가지 않아 해석하는 데 시간을 좀 썼다.

  • 생각을 해본 결과 의미는 main thread가 아닌 다른 thread 즉, global thread 에서 UIWindowScenewindows를 열거할 수 없다.
  • 열거할 수 없다라는 말은 좀 더 공부를 해봐야갰지만 말 그대로 열거 즉, 보여주면 안된다?(작성하면 안된다? 생성하면 안된다?) 무슨 말일까 생각해보면 어쨌든 계층 구조를 살펴봤을 때
  • UIScreen - UIWindowScene - UIWindow - UIView 의 순서로 쌓여진다.

  • 이 결과, UIView가 메모리에 올라가는 상황, 인스턴스가 생성되는 시점 자체가 global thread 가 아닌 main thread 여야만 했던 것이 아닐까 라는 생각이 든다

📚 문제

  • 그렇다면 나의 코드에서는 어떤 부분이 문제였을까?
  • 그렇다 위의 코드를 다시 살펴보면 나는 present() 즉, 사용자가 보는 화면에 보여지는 시점에 global thread 였던 현재의 threadmain thread 로 변경해주었다. 시점 자체가 잘못되었던 것이다.
    // 1. alert 창 구성
    let alert = UIAlertController(title: title,
                                  message: message,
                                  preferredStyle: .alert)
    // 2. alert button 구성
    let check = UIAlertAction(title: "확인", style: .default)
    // 3. alert에 button 추가
    alert.addAction(check)

    DispatchQueue.main.async { [weak self] in
        guard let self else { return }
        present(alert, animated: true)
    }
  • UIAlertControllerUIViewController를 상속받고 있는 객체이고, UIViewControllerUIView를 base로 구성되는 객체였기 때문에 나는 UIAlertController인스턴스를 생성해줬던 시점이 문제였던 것이다

⭐️ 문제 해결

  • global thread에서 main thread로 변경해주던 시점UIAlertController인스턴스를 생성하는 시점으로 변경하면서 경고문구가 사라졌다.
DispatchQueue.main.async { [weak self] in
    guard let self else { return }

    // 1. alert 창 구성
    let alert = UIAlertController(title: title,
                                  message: message,
                                  preferredStyle: .alert)
    // 2. alert button 구성
    let check = UIAlertAction(title: "확인", style: .default)

    // 3. alert에 button 추가
    alert.addAction(check)

    present(alert, animated: true)
}

 

  • 결과