앱 전체에서 사용할 값을 저장하는 방법

Core Data나 기타 DB 혹은 UserDefaults나 파일 저장 등으로 값을 저장하고 불러오는 방식 등 앱 전체에서 데이터를 저장하고 불러오는 방법은 다양하다.

그 중, AppDelegate 객체를 이용해 값을 저장하고, 여러 뷰에서 불러올 수 있는 방법을 배웠다.

AppDelegate

Swift 공부하다보면 (물론 다른 컴퓨터 프로그래밍 언어들이 다 그렇겠지만…) 객체나 메서드 네이밍이 (길지만) 직관적이라고 느껴진다. AppDelegate 또한 이름이 곧 기능인데, 앱 전반의 동작에 관여하는, 일종의 앱 대리자라고 할 수 있다.

이전 포스팅에서 사용자가 앱 아이콘을 터치해 앱이 시작되면, UIApplicationMain이 실행되고, 이 메인은 UIApplication객체를 생성하고, AppDelegate를 생성한 뒤, 해당 클래스에 있는 application 메소드를 실행시켜 앱이 처음 구동될 때 개발자가 써 놓은 코드를 실행하게 한다.

AppDelegateUIApplication의 권한을 위임받아 커스텀 코드와 상호작용하여 코드를 실제 뷰와 컨트롤러로 보여지게, 그리고 동작하게 한다. 앱 실행동안 유지되며, 앱이 종료되면 소멸하고, 단 한개의 인스턴스만 생성한다. 라고 AppDelegate의 기능을 설명했다.

단 한개의 인스턴스만 생성되는 AppDelegate는 메인문에 의해 생성되기 때문에 우리가 따로 생성하지 않는다. 마치 전역변수처럼 그 어떤 뷰 컨트롤러에서든지 AppDelegate 객체에 접근할 수 있는 것이다.

어떻게 저장하지?

AppDelegate에 저장하고자 하는 값을 정의해두고, 저장하고자 하는 시점에서 AppDelegate 객체를 호출해 해당 값을 불러오고, 갱신하면 된다.


let appDelegate = UIApplication.shared.delegate as? AppDelegate

다음과 같이 쓰면 된다. 이 때, AppDelegate의 타입이 UIApplicationDelegate이므로, 안에 있는 값들에 접근하기 위해선 다운캐스팅을 통해 AppDelegate 타입이라는 것을 명시해야 한다.

UI와 코드

UI

첫 번째 뷰에 이름, 인간인지 아닌지, 나이를 입력받고, 이를 미리 확인할 수 있는 버튼과 다음 뷰로 제출할 수 있는 버튼을 만들었다.

두 번째 뷰에선 첫 번재 뷰에서 제출한 값을 받아 각각의 라벨에 띄워주도록 했다.

storyboard

코드

우선, 원하는 데이터를 Model에 정의했다.

// Model.swift

struct Info {
    var name: String
    var isHuman: Bool
    var age: Int
}

이를 AppDelegate에 선언해 위 스토리보드의 첫 번째와 두 번째 뷰에서 접근할 수 있도록 했다.

// AppDelegate.swift

import UIKit

@main
class AppDelegate: UIResponder, UIApplicationDelegate {
    
    var infos = Infos(name: "", isHuman: true, age: 0)

    func application ... (생략)
}

각각의 뷰 컨트롤러에 각 라벨, 버튼 등과 연결해 주고, AppDelegate에 값을 저장하고, 이로부터 값을 불러올 수 있도록 했다.

// FirstViewController.swift

import UIKit

class ViewController: UIViewController {
    @IBOutlet var nameField: UITextField!
    @IBOutlet var switchStateLabel: UILabel!
    @IBOutlet var ageLabel: UILabel!
    
    @IBOutlet var switchStatus: UISwitch!
    @IBOutlet var age: UIStepper!
    
    @IBOutlet var namePreview: UILabel!
    @IBOutlet var switchPreview: UILabel!
    @IBOutlet var agePreview: UILabel!
    
    override func viewDidLoad() {
        super.viewDidLoad()
    }
    
    @IBAction func toggleSwitch(_ sender: UISwitch) {
        if sender.isOn {
            switchStateLabel.text = "YES"
        } else {
            switchStateLabel.text = "NO"
        }
    }
    
    @IBAction func stepAge(_ sender: UIStepper) {
        let age = Int(sender.value)
        ageLabel.text = String(age)
    }
    
    @IBAction func previewLabels(_ sender: Any) {
        self.namePreview.text = nameField.text
        self.switchPreview.text = switchStateLabel.text
        self.agePreview.text = ageLabel.text ?? "0" + " years old"
    }
    
    // submit 하는 버튼에서 appDelegate에 값 저장.
    @IBAction func submitInfos(_ sender: Any) {
    
        guard let secondVC = self.storyboard?.instantiateViewController(withIdentifier: "SecondPageVC") as? SecondPageVC else {
            return
        }
        let appDelegate = UIApplication.shared.delegate as? AppDelegate

        appDelegate?.infos.name = nameField.text ?? ""
        appDelegate?.infos.isHuman = switchStatus.isOn
        appDelegate?.infos.age = Int(age.value)

        self.navigationController?.pushViewController(secondVC, animated: true)
    }
}
// SecondViewController.swift

import UIKit

class SecondPageVC: UIViewController {
	
    @IBOutlet var nameLabel: UILabel!
    @IBOutlet var switchStatusLabel: UILabel!
    @IBOutlet var ageLabel: UILabel!
    
	// 뷰의 초기 상태를 만들어낼 때 AppDelegate에서 값 가져옴.
    override func viewDidLoad() {
        super.viewDidLoad()
        
        let appDelegate = UIApplication.shared.delegate as? AppDelegate
        
        nameLabel.text = appDelegate?.infos.name
        switchStatusLabel.text = appDelegate?.infos.isHuman ?? true ? "YES" : "NO"
        ageLabel.text = "\(appDelegate?.infos.age ?? 0) years old"
    }
    
    @IBAction func backToEditPage(_ sender: Any) {
        self.navigationController?.popViewController(animated: true)
    }
}

결론

  • AppDelegate는 앱 내에 단 하나만 있고, main문에 의해 자동으로 생성된 AppDelegate 인스턴스는 UIApplication.shared.delegate 구문으로 어디서든 참조할 수 있다.

  • 앱이 시작되고 끝나기 직전까지만 그 값이 유지되므로, 영속적인 값을 저장하기엔 적합하지 않다.

그 외 TIL…

@IBAction의 sender를 Any가 아닌 특정 타입으로 지정해주면 좋은 점

UIStoryboardSegue를 sender로 보내는 함수를 정의만 하면 원하는 뷰 컨트롤러로 바로 이동할 수 있다!?

댓글남기기