오늘 공부한 UIKit - AppDelegate 사용해 값 저장하기
앱 전체에서 사용할 값을 저장하는 방법
Core Data나 기타 DB 혹은 UserDefaults나 파일 저장 등으로 값을 저장하고 불러오는 방식 등 앱 전체에서 데이터를 저장하고 불러오는 방법은 다양하다.
그 중, AppDelegate
객체를 이용해 값을 저장하고, 여러 뷰에서 불러올 수 있는 방법을 배웠다.
AppDelegate
Swift 공부하다보면 (물론 다른 컴퓨터 프로그래밍 언어들이 다 그렇겠지만…) 객체나 메서드 네이밍이 (길지만) 직관적이라고 느껴진다. AppDelegate 또한 이름이 곧 기능인데, 앱 전반의 동작에 관여하는, 일종의 앱 대리자라고 할 수 있다.
이전 포스팅에서 사용자가 앱 아이콘을 터치해 앱이 시작되면, UIApplicationMain
이 실행되고, 이 메인은 UIApplication
객체를 생성하고, AppDelegate
를 생성한 뒤, 해당 클래스에 있는 application
메소드를 실행시켜 앱이 처음 구동될 때 개발자가 써 놓은 코드를 실행하게 한다.
AppDelegate
는 UIApplication
의 권한을 위임받아 커스텀 코드와 상호작용하여 코드를 실제 뷰와 컨트롤러로 보여지게, 그리고 동작하게 한다. 앱 실행동안 유지되며, 앱이 종료되면 소멸하고, 단 한개의 인스턴스만 생성한다. 라고 AppDelegate의 기능을 설명했다.
단 한개의 인스턴스만 생성되는 AppDelegate는 메인문에 의해 생성되기 때문에 우리가 따로 생성하지 않는다. 마치 전역변수처럼 그 어떤 뷰 컨트롤러에서든지 AppDelegate 객체에 접근할 수 있는 것이다.
어떻게 저장하지?
AppDelegate에 저장하고자 하는 값을 정의해두고, 저장하고자 하는 시점에서 AppDelegate 객체를 호출해 해당 값을 불러오고, 갱신하면 된다.
let appDelegate = UIApplication.shared.delegate as? AppDelegate
다음과 같이 쓰면 된다. 이 때, AppDelegate의 타입이 UIApplicationDelegate
이므로, 안에 있는 값들에 접근하기 위해선 다운캐스팅을 통해 AppDelegate
타입이라는 것을 명시해야 한다.
UI와 코드
UI
첫 번째 뷰에 이름, 인간인지 아닌지, 나이를 입력받고, 이를 미리 확인할 수 있는 버튼과 다음 뷰로 제출할 수 있는 버튼을 만들었다.
두 번째 뷰에선 첫 번재 뷰에서 제출한 값을 받아 각각의 라벨에 띄워주도록 했다.
코드
우선, 원하는 데이터를 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
구문으로 어디서든 참조할 수 있다. -
앱이 시작되고 끝나기 직전까지만 그 값이 유지되므로, 영속적인 값을 저장하기엔 적합하지 않다.
댓글남기기