본문 바로가기
iOS/UIKit

[UIKit] 값을 새로 저장해도 왜 화면에 나타나지 않을까(UserDefault와 UIViewController 생명 주기)

by Dev.Andy 2023. 7. 26.

머리말

앱에 대한 설명

구현한 앱은 "나의 기분"이라는 탭에서 "감정을 나타내는 이모티콘 버튼"을 눌렀을 때, "기본 통계"라는 탭에서 얼마나 눌렀는지를 확인 할 수 있는 앱이다.

왼쪽의 화면에 감정 이모티콘을 탭하면 오른쪽에 탭한 개수가 저장되는 앱이다

데이터의 저장 방법은 UserDefault

데이터를 저장하는 것에는 여러 방법이 있겠지만 여기서는 강의 시간에 배운 UserDefault를 이용했다. UserDefault에 대한 설명은 공식 문서에서 아래와 같다.

An interface to the user’s defaults database, where you store key-value pairs persistently across launches of your app.
애플리케이션이 실행될 때 계속해서 키-값을 쌍으로 한 형태로 이를 저장하는, 사용자의 기본 데이터베이스에 관한 인터페이스.

UserDefaults | Apple Developer Documentation

쉽게 말하면 말 그대로 사용자(user)에 대한 기본 설정 값(defaults system)을 저장한다. UserDefault는 앱이 종료되더라도 사용자가 선택한 경량의 데이터(자동 로그인 여부나 알림 수신 여부 등)를 보관하는 클래스(class)이다. 기존의 데이터는 앱이 실행되어 메모리(RAM)에만 올라가고 앱이 종료되면 데이터 또한 같이 사라지지만, UserDefault를 이용하면 해당 데이터를 반영구적으로 보관할 수 있다.

UserDefault를 이용한 코드

  1. 아래처럼 ".standard"라는 프로퍼티를 이용해 사용자의 기본값에 대한 객체(User Defaults Object)를 접근(get)하고, 원하는 타입의 키에 해당하는 값(.integer, .string 메서드를 이용해 접근)을 가져온다.
  2. 1번에서 가져온 데이터를 상황에 맞게 가공한 다음 ".set"라는 메서드를 이용하여 가공한 기본 값을 세팅(Setting Default Values)하여 다시 데이터에 저장한다.
//  WriteViewController.swift

import UIKit

class WriteViewController: UIViewController {
    
    override func viewDidLoad() {
        super.viewDidLoad()
    }
    
    @IBAction func emotionButtonTapped(_ sender: UIButton) {
        
        let value = UserDefaults.standard.integer(forKey: "\(sender.tag)")
        let result = value + 1
    
        UserDefaults.standard.set(result, forKey: "\(sender.tag)")
    }
}

내 코드의 문제점

내가 구현한 기능은, 버튼을 클릭하는 정보(emotionCount)를 처음 앱이 실행되었을 때 해당 데이터를 갖고 오기는 하지만, 이후에 버튼을 클릭했을 때 업데이트가 되지 않는 문제가 발생한다.

에러 발생 코드

이는 viewDidLoad()에서 showEmotionCount()를 호출하기 때문이다.

//  StatisticsViewController.swift

import UIKit

class StatisticsViewController: UIViewController {

    @IBOutlet var emotionCountLabelList: [UILabel]!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        showEmotionCount()
    }
    
    func showEmotionCount() {
        for i in 0..<emotionCountLabelList.count {
            emotionCountLabelList[i].text = "\(UserDefaults.standard.integer(forKey: "\(i)"))"
        }
    }
}

UIViewController 생명 주기

이를 알려면 UIViewController 클래스에 대한 생명 주기를 알아야 하는데, view가 변화하는 4가지 상태(Disappeared, Appearing, Appeared, Disappearing)에 따라서 5가지의 메서드(viewWillAppear, viewIsAppearing, viewDidAppear, viewWillDisappear, viewDidDisappear)가 호출된다.

view의 변화에 따라 다르게 호출되는 메서드

UIViewController | Apple Developer Documentation

viewDidLoad()

눈치 챈 사람도 있겠지만, 저 생명 주기의 이미지에서는 viewDidLoad()는 없다. 왜냐하면 이는 처음 view가 메모리에 올라갔을 때 처음이자 마지막으로 호출되는 메서드이기 때문이다.

이에 대한 공식 문서의 설명은 아래와 같다.

Called after the controller's view is loaded into memory.
UIViewController의 view가 메모리에 올라갔을 때 호출이 된다.

viewDidLoad() | Apple Developer Documentation

내 코드는 앱이 처음 실행되었을 때에만 view가 메모리에 올라가서 "처음이자 마지막으로" showEmotionCount() 메서드가 호출된다. 따라서 아무리 화면을 넘긴다 해도 다시는 호출이 되지 않아서 데이터의 값이 변하지 않는 것이다.

에러 해결 코드

따라서 간단하게 view가 사라졌다가 나타나면서(Disappeared → Appearing) 호출되는 viewWillAppear() 메서드에 showEmotionCount()를 호출했다. 이러면 탭 바를 눌러서 view가 전환되고 돌아왔을 때 매번 호출되어 업데이트 된 값이 label에 나타난다.

//  StatisticsViewController.swift

import UIKit

class StatisticsViewController: UIViewController {

    @IBOutlet var emotionCountLabelList: [UILabel]!
    
    override func viewDidLoad() {
        super.viewDidLoad()
    }
    
    // viewWillAppear()에서 showEmotionCount() 호출
    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        showEmotionCount()
    }
    
    func showEmotionCount() {
        for i in 0..<emotionCountLabelList.count {
            emotionCountLabelList[i].text = "\(UserDefaults.standard.integer(forKey: "\(i)"))"
        }
    }
}

 

댓글