1. View Controller Lifecycle πŸ‘©β€πŸ’»

1. View Controller Lifecycle

UIViewController-Life-Cycle

UIStoryboard & UIKitκ³Ό ν•¨κ»˜ μ‚¬μš©λ˜μ–΄, View 의 Life Cycle 을 κ΄€λ¦¬ν•œλ‹€.

이것은 Frontend λ₯Ό ν•  λ•Œ Angular, React, Vue μ—μ„œ μ‚¬μš©ν•˜λŠ” Lifecycle Hooks 와 거의 λ™μΌν•œ 역할을 ν•œλ‹€. λΈŒλΌμš°μ €(window) κ°€ μ•„λ‹Œ View Components λ₯Ό κ΄€λ¦¬ν•˜λŠ” 것이 Frontend μ—μ„œμ˜ Lifecycle Hooks 인데 Swift 의 UIViewController Lifecycle μ—­μ‹œ 이와 λ™μΌν•˜κ²Œ μ•± 자체의 ν™”λ©΄ 관리가 μ•„λ‹Œ, UIStoryboard λΌλŠ” κ²ƒμ˜ Life Cycle 을 κ΄€λ¦¬ν•œλ‹€.


1 ) viewDidLoad

  • View Instance κ°€ λ©”λͺ¨λ¦¬μ— λ‘œλ“œλœ 직후 호좜.
  • View κ°€ destroyed λ˜μ§€ μ•ŠλŠ” ν•œ 1번만 호좜.
  • View 와 κ΄€λ ¨λœ 1νšŒμ„±μ˜ μ΄ˆκΈ°ν™” μž‘μ—…, λ„€νŠΈμ›Œν¬ 호좜 등을 처리.


2 ) viewWillAppear

  • View κ°€ 화면에 보이기 직전 맀번 호좜.
  • View κ°€ destroyed λ˜μ§€ μ•Šλ”λΌλ„ λ‹€λ₯Έ View κ°€ 보여지닀 μƒˆλ‘­κ²Œ λ³΄μ—¬μ§ˆ λ•Œλ§ˆλ‹€ 호좜.
  • View κ°€ 보일 λ•Œλ§ˆλ‹€ 맀번 μ‹€ν–‰λ˜μ–΄μ•Όν•˜λŠ” μ΄ˆκΈ°ν™” μž‘μ—…, λ„€νŠΈμ›Œν¬ 호좜 등을 처리.


3 ) viewDidAppear

  • View κ°€ 화면에 보이기 μ‹œμž‘ν•  λ•Œ 호좜.
  • View κ°€ 화면에 보여지기 μ‹œμž‘ν•˜λŠ” μ‹œμ μ— μž‘λ™ν•  μ• λ‹ˆλ©”μ΄μ…˜ 등을 처리.


4 ) viewWillDisappear

  • View κ°€ ν™”λ©΄μ—μ„œ 사라지기 μ‹œμž‘ν•  λ•Œ 호좜.
  • View κ°€ 사라지기 직전 μ΄ˆκΈ°ν™” μž‘μ—…, λ„€νŠΈμ›Œν¬ 호좜, 데이터 μ €μž₯ 등을 처리.


5 ) viewDidDisappear

  • View κ°€ 사라진 λ‹€μŒ 호좜.
  • View Instance κ°€ λ©”λͺ¨λ¦¬μ—μ„œ 제거된 이후 좔가적인 μž‘μ—… ν•„μš”μ‹œ 처리.

2. Examples - Present Modally - Automatic

  • ViewController1
class ViewController1: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        print("VC1 viewDidLoad Called")
    }

    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        print("VC1 viewWillAppear Called")
    }

    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
        print("VC1 viewDidAppear Called")
    }

    override func viewWillDisappear(_ animated: Bool) {
        super.viewWillDisappear(animated)
        print("VC1 viewWillDisappear Called")
    }

    override func viewDidDisappear(_ animated: Bool) {
        super.viewDidDisappear(animated)
        print("VC1 viewDidDisappear Called")
    }

}
  • ViewController2
class ViewController2: UIViewController {

    @IBOutlet weak var label: UILabel!

    @IBAction func goBack(_ sender: UIButton) {
        dismiss(animated: true, completion: nil)
    }

    override func viewDidLoad() {
        super.viewDidLoad()

        label.text = "hello"

        print("VC2 viewDidLoad Called")
    }

    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        print("VC2 viewWillAppear Called")
    }

    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
        print("VC2 viewDidAppear Called")
    }

    override func viewWillDisappear(_ animated: Bool) {
        super.viewWillDisappear(animated)
        print("VC2 viewWillDisappear Called")
    }

    override func viewDidDisappear(_ animated: Bool) {
        super.viewDidDisappear(animated)
        print("VC2 viewDidDisappear Called")
    }

}


View-Controller-Life-Cycle-Modal

1 ) VC1 λ‘œλ”©

VC1 viewDidLoad Called
VC1 viewWillAppear Called
VC1 viewDidAppear Called


2 ) VC2둜 이동

VC2 viewDidLoad Called
VC2 viewWillAppear Called
VC2 viewDidAppear Called


3 ) VC1둜 λ˜λŒμ•„κ°€κΈ°

Back λ²„νŠΌμ„ 눌러 μ΄λ™μ‹œ

VC2 viewWillDisappear Called
VC2 viewDidDisappear Called


그런데 Modal 은 View Controller 의 Life Cycle μ—μ„œ 쑰금 νŠΉμ΄ν•œ 뢀뢄이 μžˆλ‹€. Modal 을 천천히 λŒμ–΄ λ‚΄λ¦¬κ±°λ‚˜ λŒμ–΄ 내리닀 λ‹€μ‹œ 올릴 경우 ν˜ΈμΆœλ˜λŠ” Life Cycle 을 보자.

  • Modal 을 λŒμ–΄λ‚΄λ¦¬λŠ” λ™μ•ˆ
VC2 viewWillDisappear Called
  • Modal 을 μ™„μ „νžˆ λŒμ–΄ λ‚΄λ € VC1으둜 μ΄λ™μ‹œ
VC2 viewDidDisappear Called

이둜써 Modal 을 천천히 λ‚΄λ € λ‘˜μ΄ ν˜ΈμΆœλ˜λŠ” μ‹œμ μ΄ λͺ…λ°±νžˆ λ‹€λ₯΄λ‹€λŠ” 것을 확인할 수 μžˆλ‹€.

  • Modal 을 λŒμ–΄ 내리닀 λ‹€μ‹œ 올렀 VC2둜 λ˜λŒμ•„μ˜¬ 경우
VC2 viewWillAppear Called
VC2 viewDidAppear Called

viewDidLoad()λŠ” ν˜ΈμΆœλ˜μ§€ μ•ŠλŠ” 것을 확인할 수 μžˆλ‹€.


4 ) λ‹€μ‹œ VC2둜 이동

VC2 viewDidLoad Called
VC2 viewWillAppear Called
VC2 viewDidAppear Called


5 ) μ•± μ“Έμ–΄μ˜¬λ¦¬κΈ°

// Nothing


6 ) μ•± 내리기(Background 둜 보내기)

// Nothing

앱을 μ“Έμ–΄ μ˜¬λ¦¬κ±°λ‚˜ λ‚΄λ Έλ‹€ λ‹€μ‹œ λ“€μ–΄μ˜€λŠ” 것은 VC 의 Lifecycle κ³ΌλŠ” λ¬΄κ΄€ν•˜λ‹€.

3. Examples - Present Modally - Full Screen

View-Controller-Life-Cycle-Full-Screen

1 ) VC1 λ‘œλ”©

VC1 viewDidLoad Called
VC1 viewWillAppear Called
VC1 viewDidAppear Called


2 ) VC2둜 이동

VC2 viewDidLoad Called
VC1 viewWillDisappear Called
VC2 viewWillAppear Called
VC2 viewDidAppear Called
VC1 viewDidDisappear Called

Modal μ—μ„œ Full Screen 으둜 λ°”κΎΈμž μ™„μ „νžˆ λ‹€λ₯Έ 행동 νŒ¨ν„΄μ„ 보여쀀닀.


3 ) VC1둜 λ˜λŒμ•„κ°€κΈ°

VC2 viewWillDisappear Called
VC1 viewWillAppear Called
VC1 viewDidAppear Called
VC2 viewDidDisappear Called

λ§ˆμ°¬κ°€μ§€λ‘œ viewDidLoad()λŠ” ν˜ΈμΆœλ˜μ§€ μ•ŠλŠ” 것을 확인할 수 μžˆλ‹€. ν•˜μ§€λ§Œ Modal κ³Ό 달리 μ™„μ „νžˆ κ°€λ €μ‘Œλ˜ VC1 을 λ‹€μ‹œ λ³΄μ΄λ©΄μ„œ viewWillAppear(_:)와 viewDidAppear(_:)κ°€ ν˜ΈμΆœλ˜λŠ” 것을 확인할 수 μžˆλ‹€.


4 ) λ‹€μ‹œ VC2둜 이동

VC2 viewDidLoad Called
VC1 viewWillDisappear Called
VC2 viewWillAppear Called
VC2 viewDidAppear Called
VC1 viewDidDisappear Called


5 ) μ•± μ“Έμ–΄μ˜¬λ¦¬κΈ°

// Nothing


6 ) μ•± 내리기(Background 둜 보내기)

// Nothing

μ€‘μš”ν•œ 것은 λ‘˜ λ‹€ λ³΄μ—¬μ£ΌλŠ” λ°©μ‹μ˜ 차이일 뿐 λͺ¨λ‘ Modal 방식을 μ‚¬μš©ν•˜κΈ° λ•Œλ¬Έμ— VC1 은 VC2 둜 가더라도 μ•„λž˜ κ°μΆ°μ§€λŠ” 것일 뿐 destroyed λ˜μ§€ μ•Šκ³  viewDidLoad()κ°€ λ‹€μ‹œ ν˜ΈμΆœλ˜μ§€ μ•ŠλŠ”λ‹€. 반면 VC2 λŠ” VC1 둜 λŒμ•„κ°ˆ λ•Œ destroyed 되기 λ•Œλ¬Έμ— VC2 둜 λ‹€μ‹œ 이동할 경우 viewDidLoad()κ°€ λ‹€μ‹œ ν˜ΈμΆœλœλ‹€.


2. App Lifecycle πŸ‘©β€πŸ’»

1. App Lifecycles

μ•±μ˜ ν˜„μž¬ μƒνƒœμ— 따라 μ–Έμ œλ“  μˆ˜ν–‰ν•  수 μžˆλŠ” μž‘μ—…κ³Ό μˆ˜ν–‰ν•  수 μ—†λŠ” μž‘μ—…μ΄ κ²°μ •λœλ‹€. 예λ₯Ό λ“€λ©΄ Foreground App은 CPU λ₯Ό ν¬ν•¨ν•œ μ‹œμŠ€ν…œ λ¦¬μ†ŒμŠ€λ³΄λ‹€ μš°μ„  μˆœμœ„κ°€ λ†’λ‹€. λ°˜λŒ€λ‘œ Background App은 κ°€λŠ₯ν•œ ν•œ 적은 μž‘μ—…μ„ μˆ˜ν–‰ν•΄μ•Όν•˜λ©°, ν™”λ©΄ 밖에 있기 λ•Œλ¬Έμ— 가급적 아무 μž‘μ—…λ„ μˆ˜ν–‰ν•˜μ§€ μ•ŠλŠ” 것이 μ’‹λ‹€. μ•±μ˜ μƒνƒœ λ³€ν™”λ₯Ό κ°μ§€ν•˜κ³  그에 λ”°λ₯Έ μž‘λ™μ„ μ‘°μ •ν•˜κΈ° μœ„ν•΄ Swift μ—μ„œλŠ” λ‹€μŒκ³Ό 같은 방법을 μ œκ³΅ν•œλ‹€.

  • UISceneDelegate : iOS 13 μ΄μƒμ—μ„œ App 의 Lifecycles λ₯Ό κ΄€λ¦¬ν•˜κΈ° μœ„ν•΄ μ‚¬μš©ν•œλ‹€.
  • UIApplicationDelegate : iOS 12 μ΄ν•˜μ—μ„œ App 의 Lifecycles λ₯Ό κ΄€λ¦¬ν•˜κΈ° μœ„ν•΄ μ‚¬μš©ν•œλ‹€.
document.addEventListener('visibilitychange', (event) => {
    if (document.hidden) {
        console.log('not visible');
    } else {
        console.log('is visible');
    }
});

iOS μ—μ„œ μ•±μ˜ μƒνƒœμ— 따라 행동을 μ œμ–΄ν•œλ‹€λŠ” 것은 Frontend μ—μ„œ λΈŒλΌμš°μ €μ™€ κ·Έ νƒ­μ˜ ν™œμ„± μƒνƒœλ₯Ό κ°μ§€ν•˜κ³  이에 λ”°λ₯Έ μž‘λ™μ„ μ‘°μ •ν•˜λŠ” 것과 κ°™λ‹€.

1 ) UISceneDelegate

app-lifecycle-scene-state


2 ) UIApplicationDelegate

app-lifecycle-app-state

μ•„λž˜ μ½”λ“œμ—μ„œ λ³Ό 수 μžˆλ“―μ΄ UISceneDelegateλŠ” UIApplicationDelegate의 application(_:didFinishLaunchingWithOptions:) λ©”μ„œλ“œμ™€ application(_:configurationForConnecting:options:) λ©”μ„œλ“œ 사이에 μœ„μΉ˜ν•œλ‹€.

2. Examples - Present Modally - Automatic

  • AppDelegate: UIResponder, UIApplicationDelegate
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {

    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        print(#function)

        return true
    }

    // MARK: UISceneSession Lifecycle

    func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration {
        print(#function)

        return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role)
    }

    func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set<UISceneSession>) {
        print(#function)
    }

}
  • SceneDelegate: UIResponder, UIWindowSceneDelegate
class SceneDelegate: UIResponder, UIWindowSceneDelegate {

    var window: UIWindow?

    func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
        print(#function)
        guard let _ = (scene as? UIWindowScene) else { return }
    }

    func sceneDidDisconnect(_ scene: UIScene) {
        print(#function)
    }

    func sceneDidBecomeActive(_ scene: UIScene) {
        print(#function)
    }

    func sceneWillResignActive(_ scene: UIScene) {
        print(#function)
    }

    func sceneWillEnterForeground(_ scene: UIScene) {
        print(#function)
    }

    func sceneDidEnterBackground(_ scene: UIScene) {
        print(#function)
    }

}


View-Controller-Life-Cycle-Modal

1 ) VC1 λ‘œλ”©

application(_:didFinishLaunchingWithOptions:)
scene(_:willConnectTo:options:)
VC1 viewDidLoad Called
VC1 viewWillAppear Called
sceneWillEnterForeground(_:)
sceneDidBecomeActive(_:)
VC1 viewDidAppear Called

앱을 μ‹œμž‘ν•¨κ³Ό λ™μ‹œμ— App Life Cycles application(_:didFinishLaunchingWithOptions:)κ³Ό scene(_:willConnectTo:options:)이 ν˜ΈμΆœλœλ‹€.

그리고 첫 번째 View κ°€ 뜨고 λ‘œλ“œ λ˜μ–΄ 뜨기 μ „ 앱이 Foreground둜 μ§„μž…ν•˜κ²Œ 되고, UISceneDelegate κ°€ 이λ₯Ό κ°μ§€ν•˜κ³  sceneWillEnterForeground(_:)와 sceneDidBecomeActive(_:)λ₯Ό ν˜ΈμΆœν•œλ‹€.


2 ) VC2둜 이동

VC2 viewDidLoad Called
VC2 viewWillAppear Called
VC2 viewDidAppear Called

앱은 κ³„μ†ν•΄μ„œ Foregroundλ₯Ό μœ μ§€ν•˜λ―€λ‘œ, App Life Cycle 은 λ³€ν™”κ°€ μ—†λ‹€.


3 ) VC1둜 λ˜λŒμ•„κ°€κΈ°

Back λ²„νŠΌμ„ 눌러 μ΄λ™μ‹œ

VC2 viewWillDisappear Called
VC2 viewDidDisappear Called

λ§ˆμ°¬κ°€μ§€λ‘œ App Life Cycle 은 λ³€ν™”κ°€ μ—†λ‹€.


4 ) μ•± μ“Έμ–΄μ˜¬λ¦¬κΈ°

sceneWillResignActive(_:)


5 ) μ•± 내리기(Background 둜 보내기)

sceneDidEnterBackground(_:)


6 ) μ•± λ‹€μ‹œ 뢈러였기(Foreground 둜 뢈러였기)

sceneWillEnterForeground(_:)
sceneDidBecomeActive(_:)


7 ) μ•± μœ„λ‘œ λ‚ λ € μ’…λ£Œν•˜κΈ°

sceneWillResignActive(_:)
sceneDidDisconnect(_:)
application(_:didDiscardSceneSessions:)
VC1 viewWillDisappear Called
VC1 viewDidDisappear Called

3. Examples - Present Modally - Full Screen

View-Controller-Life-Cycle-Full-Screen

1 ) VC1 λ‘œλ”©

application(_:didFinishLaunchingWithOptions:)
scene(_:willConnectTo:options:)
VC1 viewDidLoad Called
VC1 viewWillAppear Called
sceneWillEnterForeground(_:)
sceneDidBecomeActive(_:)
VC1 viewDidAppear Called


2 ) VC2둜 이동

VC2 viewDidLoad Called
VC1 viewWillDisappear Called
VC2 viewWillAppear Called
VC2 viewDidAppear Called
VC1 viewDidDisappear Called


3 ) VC1둜 λ˜λŒμ•„κ°€κΈ°

Back λ²„νŠΌμ„ 눌러 μ΄λ™μ‹œ

VC2 viewWillDisappear Called
VC1 viewWillAppear Called
VC1 viewDidAppear Called
VC2 viewDidDisappear Called


4 ) μ•± μ“Έμ–΄μ˜¬λ¦¬κΈ°

sceneWillResignActive(_:)


5 ) μ•± 내리기(Background 둜 보내기)

sceneDidEnterBackground(_:)


6 ) μ•± λ‹€μ‹œ 뢈러였기(Foreground 둜 뢈러였기)

sceneWillEnterForeground(_:)
sceneDidBecomeActive(_:)


7 ) μ•± μœ„λ‘œ λ‚ λ € μ’…λ£Œν•˜κΈ°

sceneWillResignActive(_:)
sceneDidDisconnect(_:)
application(_:didDiscardSceneSessions:)
VC1 viewWillDisappear Called
VC1 viewDidDisappear Called




Reference

  1. Angela Yu, β€œiOS & Swift - The Complete iOS App Development Bootcamp, Section 15.” Udemy.com. last modified Nov. 2021, https://www.udemy.com/course/ios-13-app-development-bootcamp/.
  2. β€œUIViewController.” Apple Developer Documentation. accessed Mar. 16, 2023, UIViewController Life Cycle.
  3. β€œManaging your app’s life cycle.” Apple Developer Documentation. accessed Mar. 16, 2023, App Life Cycle.