DispatchQueue.main.async 에서 Task로 바꿔서 UI Update하기

Zion·2022년 11월 9일
0
post-thumbnail

dismiss의 역할은 각자의 viewController에서 처리해주는게 좋다.

그러나 ..

불가피하게 다른 viewController에서 dismiss작업을 처리해줘야한다면?..

근데 그게 한두개가 아니고 여러개라면?

dismiss동작이 순차적으로 animating되어야 한다면?

이라는 고민에 빠졌다.

함수 안의 코드를 볼때 순차적으로 진행되는것처럼

func viewsDismiss() {
	popupController.dimiss(animated: true) {
    	loginController.dismiss(animaed: true) {
        	menuController.dismiss(animated: true) {
            	mainViewLoginAskController.dismiss(animated: true) {
                //...
                }
            	
            }
        }
    }
 
}

순차적으로 진행되야 한다면...
이런 completion depth가 다중으로 생긴다.☠️☠️☠️

func requestLogin() {
	apiHandler.request { isSuccess in
	    DispatchQueue.main.async {
			if isSuccess {
    			self.viewsDimiss()
		    } else {
    	    	self.floatingErrorMessage()
        	}
    	}
    }
}

login request가 성공했을 경우 viewsDismiss 를 호출하고..
실패할경우엔 errorMessage를 띄워주게 하는 동작이다.

viewsDimissfloatingErrorMessage동작이 다 UI관련 작업이라 Main Thread에서 실행되어야 하므로 DispatchQueue.main.async로 감쌌다.

나는.. viewsDimiss의 다중 completion이 싫다..!
고로..

func viewsDismiss() async {
        await withCheckedContinuation { continuation in
            popupController.dismiss(animated: true) {
                continuation.resume()
            }
        }
        
        await withCheckedContinuation { continuation in
            loginController.dismiss(animated: true) {
                continuation.resume()
            }
        }
        
        await withCheckedContinuation { continuation in
            menuController.dismiss(animated: true) {
                continuation.resume()
            }
        }
        
        await withCheckedContinuation { continuation in
            mainViewLoginAskController.dismiss(animated: true) {
                continuation.resume()
            }
        }
}

이렇게 async한 동작으로 바꼈다.

기존 viewsDimiss를 대체해보자 그럼.


그래. 그럼 await를 붙여주자.

func requestLogin() {
	apiHandler.request { isSuccess in
	    DispatchQueue.main.async {
			if isSuccess {
    			await self.viewsDimiss()
		    } else {
    	    	self.floatingErrorMessage()
        	}
    	}
    }
}

...흠

알고 있었다. async함수는 DispatchQueue.main.async에선 쓸 수 없다..

그렇담
Task를 사용해야 하는데.. 저번에 브레이크 포인트로 Task내부 함수를 찍어봤을때 쯔어 멀리 76..번째쯤?.. Thread에서 함수가 실행됐었다.

잠깐. 나는 UI update를 해야해!! Task의 작업은 main thread에서 실행되어야돼!! 하고 찾아봤다.

오 나와 같은 고민을 하고 있군..

답변의 핵심
- DispatchQueue.main.async 무지성 호출 패턴 졸업해라
- @MainActor사용 해라..
- ♨️♨️♨️ Task를 DispatchQueue.global().async처럼 행동한다고 생각 하지마라. 아니니까.( 이거봐라. WWDC2021)

고로 최종 코드는

func requestLogin() {
	apiHandler.request { isSuccess in
	    Task {
			if isSuccess {
    			await self.viewsDimiss()
		    } else {
    	    	await floatingErrorMessage()
        	}
    	}
    }
}

@MainActor
func viewsDismiss() async {
//... 맨 마지막 함수랑 내용은 같음.
}

이다.

/TODO: 아래 영상 요약 포스팅
https://developer.apple.com/videos/play/wwdc2021/10194
/

참고:
https://stackoverflow.com/questions/44236473/insert-authorization-header-field-with-kingfisher-lib

https://gyuios.tistory.com/183

틀린내용 있으면 정정댓글 환영입니다. 혼자만 알지 말고 같이 알려줘요.

profile
어제보다만 나아지는

0개의 댓글