-
[Swift] 메인쓰레드와 UI 작업 | UILabel.text must be used from main thread only에러처리 2023. 3. 2. 11:13
시작하며
Swift를 공부하면서 콘텐츠가 풍부한 유튜버마냥 하루에도 몇번이나 에러가 뜬다. 블로그에 쓸 내용이 마를 일은 없을 듯 하다.
간단한 Weather 앱을 만들기 위해 비동기로 Open Weather를 통해 데이터를 받아왔다. 받아온 데이터를 그대로 IBOutlet으로 연결된 label의 text에 담는 과정에서 위와 같은 에러가 발생했다. 즉, UI을 새로 그리는 과정에서 에러가 발생했다.
import UIKit class ViewController: UIViewController { @IBOutlet weak var countryLabel: UILabel! @IBOutlet weak var regionLabel: UILabel! @IBOutlet weak var currentTempLabel: UILabel! @IBOutlet weak var currentWeatherLabel: UILabel! @IBOutlet weak var minTempLabel: UILabel! @IBOutlet weak var maxTempLabel: UILabel! //네트워크를 도와주는 manager let weatherManager = WeatherManager.shared //⭐️ 에러 발생 지점 ⭐️ var weatherData: WeatherData? { didSet { countryLabel.text = "\(weatherData?.sys.country ?? "KR")" regionLabel.text = "\(weatherData?.name ?? "Seoul")" let currentTemp = (weatherData?.main.temp ?? 0) - 273.15 let minTemp = (weatherData?.main.tempMin ?? 0) - 273.15 let maxTemp = (weatherData?.main.tempMax ?? 0) - 273.15 currentTempLabel.text = "\(currentTemp)\(WeatherAPI.celcius)" currentWeatherLabel.text = "\(weatherData?.weather[0].main ?? "Sunny")" minTempLabel.text = "\(minTemp)\(WeatherAPI.celcius)" maxTempLabel.text = "\(maxTemp)\(WeatherAPI.celcius)" } } override func viewDidLoad() { super.viewDidLoad() setupData() setupConfigureUI() } //기본 데이터 설정 func setupConfigureUI(){ countryLabel.text = "KR" regionLabel.text = "Seoul" currentTempLabel.text = "0\(WeatherAPI.celcius)" currentWeatherLabel.text = "Sunny" minTempLabel.text = "0\(WeatherAPI.celcius)" maxTempLabel.text = "0\(WeatherAPI.celcius)" } //데이터 받아오기 func setupData(){ weatherManager.getWeatherInfo { result in switch result { case .success(let weather): self.weatherData = weather print("get weather data") case .failure(let error): print(error.localizedDescription) } } } }
UI 작업은 main Thread 에서만 이루어져야 한다.
- UIKit에서 main run loop라는 런 루프를 통해 앱 내에서 발생하는 이벤트들을 담당하게 되는데 이 main run loop에서 60초에 1번 Update cycle에 따라 화면을 새로 그리게 된다.
- 여러개의 Thread에서 동시적으로 UI 변경 요청이 발생하는 경우 한번에 GPU가 처리할 수 없어 성능상에 문제가 생길 수 있기 때문에 Serial Queue인 main Thread에서만 UI 작업이 이루어진다.
Main Thread로 UI 작업 보내기
에러가 발생한 부분을 보면 비동기로 데이터를 받아온 뒤(비동기로 작업을 하고 있기 때문에 메인쓰레드가 아닌 다른 쓰레드에서 작업 중) 바로 UI작업을 진행했기 때문에 에러가 발생하였다. 아래와 같이 DispathQueue.main.async를 통해 main thread로 UI 작업을 요청하는 것으로 에러를 해결할 수 있다.
func setupData(){ weatherManager.getWeatherInfo { result in switch result { case .success(let weatherData): DispatchQueue.main.async { [weak self] in self?.countryLabel.text = "\(weatherData.sys.country)" self?.regionLabel.text = "\(weatherData.name)" let currentTemp = round((weatherData.main.temp) - 273.15) let minTemp = round((weatherData.main.tempMin) - 273.15) let maxTemp = round((weatherData.main.tempMax) - 273.15) self?.currentTempLabel.text = "\(Int(currentTemp))\(WeatherAPI.celcius)" self?.minTempLabel.text = "\(Int(minTemp))\(WeatherAPI.celcius)" self?.maxTempLabel.text = "\(Int(maxTemp))\(WeatherAPI.celcius)" let currentWeather = weatherData.weather[0].main self?.currentWeatherLabel.text = "\(currentWeather)" self?.backgroundImageView?.image = UIImage(named: currentWeather) self?.descriptionLabel.text = WeatherDescriptionAPI.calcDesc(mainDescription: currentWeather) } print("get weather data") case .failure(let error): print(error.localizedDescription) } } }
References
https://neph3779.github.io/ios/WhyUIOnlyInMainThread/
왜 UI작업은 main thread에서 해야할까? - Neph's iOS blog
UIKit은 Nonatomic
neph3779.github.io
UI작업이 Main Thread에서 이뤄져야 하는 이유
iOS에서 개발을 진행하다 보면, UI 업데이트와 관련된 모든 작업은 main thread에서 작업을 해야 한다는 걸 많이 마주치게 될 것이다. 항상 이에 관한 정확한 이유를 모르고 넘겼지만 이번 기회에 이
ios-dev-skyline-23.tistory.com
'에러처리' 카테고리의 다른 글
[Swift] "No such module" 에러 / SPM && WatchOS (0) 2023.10.14 [Swift] Exception NSException "keypath data not found in entity _" (0) 2023.02.28 [Swift] outlet connection error / 스토리보드 파일 연결 에러 (0) 2023.02.27 [Git] git merge --no-ff Xcode 에러 해결하기 (0) 2023.02.04 [netlify] 호스팅 exit code 1 에러 해결하기 (0) 2023.01.09