일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
Tags
- EventLoop
- isolate
- SwiftUI
- SampleApp
- iot
- AppleDeveloper
- GIT
- flutter
- swift
- state
- dartz
- Architecture
- Adapter
- dart
- singleton
- WWDC24
- concurrency
- builder
- philipshue
- Xcode
- tuist
- LifeCycle
- weatherkit
- factory
- OpenAI
- WiFi
- 문법
- designpattern
- network
- uikit
Archives
- Today
- Total
Jaebi의 Binary는 호남선
App Lifecycle 본문
목차
iOS App Lifecycle
- iOS 앱의 생명주기는 크게 3가지로 분류
- Not Running → 앱이 실행되고 있지 않은 상태
- Foreground → 앱이 실행되어 화면을 차지 하고 있는 상태
- InActive → 외부적인 방해로 Full Control 불가능
- Active → 앱이 화면을 차지하고 있으면서 앱을 Full Control 가능
- Active 상태가 되거나 벗어날때는 InActive를 반드시 거쳐가야함
- Background → 앱이 메모리를 차지하고 있지만 화면을 차지하지 않은 상태
- Running → 화면을 차지하지 않더라도 Background에서 게속 작업을 수행
- Suspend → 앱이 메모리에서 아무 행동을 하지 않으면서 사용자가 앱을 다시 실행할 때까지 대기
Flow | 예시 상황 |
Not Running → InActive → Active | 앱 실행 하여 초기 페이지 진입 |
Active → InActive | 앱이 켜져있을때 앱 스위처로 이동 |
Active → InActive → Running | 음악 플레이어 켜져있을때 체스처로 플레이어 백그라운드로 보냄 |
Running → Not Running (Timeout) | 앱이 background에서 작업을 정해진 시간 내에 완료 못하면 Timeout 함수 호출, Not Running 상태로 됨 |
Running → Suspend | 앱이 background에서 작업을 정해진 시간 내에 완료하면 Suspend 상태로 됨 |
Active → InActive → Suspend | 음악 플레이어를 정지하고 체스처로 플레이어 백그라운드로 보냄 |
Suspend → Not Running (메모리 부족) | Suspend상태에서 메모리 용량이 부족하면 OS의 결정으로 앱이 메모리에서 내려가 Not Running 상태로 됨 |
Running → Not Running InActive → Not Running |
Background에 있는 앱 종료 앱 스위처에서 드래그 제스처로 종료 |
SwiftUI에서 App Lifecycle 확인
- `@Environment` property wrapper를 사용하여 `ScenePhase`를 가져오고 `onChange(of:)` modifier로 상태 확인
struct ScenePhaseApp: App {
@Environment(\.scenePhase) var scenePhase
var body: some Scene {
WindowGroup {
ContentView()
}
.onChange(of: scenePhase) { newScenePhase in
switch newScenePhase {
case .active:
print("active")
case .inactive:
print("inactive")
case .background:
print("background")
@unknown default:
print("unknown")
}
}
}
}
- `NotificationCenter` → 등록된 event가 발생하면 해당 event에 대한 행동을 취함
- `NotificationCenter`를 사용하여 `UIApplication`을 observe
struct NotificationCenterApp: App {
var body: some Scene {
WindowGroup {
ContentView()
.onReceive(NotificationCenter.default.publisher(for: UIApplication.didBecomeActiveNotification)) { (_) in
// Active 상태
print("did become active")
}
.onReceive(NotificationCenter.default.publisher(for: UIApplication.willResignActiveNotification)) { (_) in
// InActive 상태
print("will resign active")
}
.onReceive(NotificationCenter.default.publisher(for: UIApplication.didEnterBackgroundNotification)) { (_) in
// Background 상태
print("did enter background")
}
.onReceive(NotificationCenter.default.publisher(for: UIApplication.willEnterForegroundNotification)) { (_) in
// Background -> Foreground 상태
print("will enter foreground")
}
.onReceive(NotificationCenter.default.publisher(for: UIApplication.willTerminateNotification)) { (_) in
// 앱 종료 시
print("will terminate")
}
}
}
}
AppDelegate와 SceneDelegate 살리기
- `AppDelegate` 클래스 작성
class AppDelegate: UIResponder, UIApplicationDelegate {
func application(
_ application: UIApplication,
willFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil
) -> Bool {
// Not Running 상태
// 앱 실행시 최초로 실행할 코드 (ex. firebase configure)
print("Not Running, will finish launching")
return true
}
func applicationDidFinishLaunching(_ application: UIApplication) {
// Not Running 상태
// 초기화 코드
print("Not Running, did finish launching")
}
func applicationWillTerminate(_ application: UIApplication) {
// Not Running 상태
// 앱이 종료되기 직전에 호출
print("Not Running, will terminate")
}
}
- `SceneDelegate` 클래스 작성
class SceneDelegate: UIResponder, UIWindowSceneDelegate {
var window: UIWindow?
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
guard let _ = (scene as? UIWindowScene) else { return }
}
func sceneDidDisconnect(_ scene: UIScene) {
// Background 상태
// Scene이 백그라운드로 갈때 iOS는 리소스를 확보하기 위헤 Scene을 삭제할 수 있음
// 특정 Scene을 Foreground로 가져올때 다시 연결 가능
print("Background, did disconnect")
}
func sceneDidBecomeActive(_ scene: UIScene) {
// Active 상태
// 앱이 실제로 사용되기 전에 마지막으로 준비할 코드
print("Active, did become active")
}
func sceneWillResignActive(_ scene: UIScene) {
// InActive 상태
// 앱 스위처 모드, 전화 왔을때 (ex. 음악을 듣고 있는데 전화가 옴 -> 여기서 음악을 정지)
print("InActive, will resign active")
}
func sceneWillEnterForeground(_ scene: UIScene) {
// InActive 상태
// Background나 Not Running에서 Foreground로 들어가기 직전에 호출
print("Active, will enter foreground")
}
func sceneDidEnterBackground(_ scene: UIScene) {
// Background 상태
// Foreground에서 Background로 갈때 호출
// user data저장 -> 앱이 종료(deallocated from RAM) 되는 시점을 예측할 수 없기 때문
print("Background, did enter background")
}
}
- `AppDelegate`에 `SceneDelegate` 연결
class AppDelegate: UIResponder, UIApplicationDelegate {
// ...
func application(
_ application: UIApplication,
configurationForConnecting connectingSceneSession: UISceneSession,
options: UIScene.ConnectionOptions
) -> UISceneConfiguration {
let sceneConfig = UISceneConfiguration(name: nil, sessionRole: connectingSceneSession.role)
sceneConfig.delegateClass = SceneDelegate.self
return sceneConfig
}
}
- `@UIApplicationDelegateAdaptor` 프로퍼티 래퍼로 만든 `AppDelegate` 넣어줌
struct AppDelegateApp: App {
@UIApplicationDelegateAdaptor var appDelegate: AppDelegate
var body: some Scene {
WindowGroup {
ContentView()
}
}
}
- Sample Test Project Link
Reference
'Swift' 카테고리의 다른 글
SwiftUI - Navigation (1) | 2024.07.23 |
---|---|
AppDelegate & SceneDelegate (0) | 2024.07.21 |
SwiftUI - App Protocol (0) | 2024.07.21 |
ARC (Automatic Reference Counting) (0) | 2024.07.20 |
Swift - Memory 기초 (0) | 2024.07.20 |