SwiftUI

SwiftUI - Observable 객체와 Environment 객체 튜토리얼

iosos 2024. 1. 27. 18:36

ObservableDemo 프로젝트

  • Observable 객체는 시간이 지남에 따라 반복적으로 변하는 데이터 (동적 데이터)를 래핑할 때 강력함
  • 이런 데이터를 시뮬레이션하기 위해서는 Foundation 프레임워크의 Timer 객체를 사용하여 매 초마다 카운터가 업데이트 되도록 구성하여 Observable 데이터 객체 생성할 것임
  • 이 카운터는 앱 프로젝트 내의 뷰들이 볼 수 있도록 게시

 

 

 

Observable 객체 추가

  • ObservableObject 프로토콜을 구현하는 데이터 클래스 추가 (New Swift File 생성)
  • TimerData 클래스
import Foundation
import Combine

class TimerData : ObservableObject {
    @Published var timeCount = 0
    var timer : Timer?
    
    init() {
        timer = Timer.scheduledTimer(timeInterval: 1.0,
                                     target: self,
                                     selector: #selector(timerDidFire),
                                     userInfo: nil,
                                     repeats: true)
    }
    
    @objc func timerDidFire(){
        timeCount += 1
    }
    
    func resetCount(){
        timeCount = 0
    }
}
  • Timer 인스턴스는 매 초마다 timerDidFire()라는 이름의 함수를 호출하도록 구성
  • timerDidFire() 함수는 timerCount 변수에 할당된 값을 1씩 증가시킴
  • timerCount 변수는 @Publish 프로퍼티 래퍼로 선언되어 프로젝트 내 뷰에서 관찰 가능

 

 

 

ContentView 레이아웃 설계

import SwiftUI

struct Chap23: View {
    
    @ObservedObject var timerData : TimerData = TimerData()
    
    var body: some View {
        **NavigationView {
            VStack{
                Text("Timer count = \(timerData.timeCount)")
                    .font(.largeTitle)
                    .fontWeight(.bold)
                    .padding()
                Button(action: resetCount){
                    Text("Reset Count")
                }
            }
        }**
    }
    
    func resetCount(){
        timerData.resetCount()
    }
}

 

 

 

 

 

 

 

동일한 Observable 객체를 접근하는 두 번째 뷰 추가

import SwiftUI

struct SecondView: View {
    
    @ObservedObject var timerData : TimerData
    
    var body: some View {
        VStack{
            Text("Second View")
                .font(.largeTitle)
            Text("Timer Count = \(timerData.timeCount)")
                .font(.headline)
        }
        .padding()
    }
}

#Preview {
    SecondView(timerData: TimerData())
}

  • SecondView는 자신만의 TimerData 인스턴스를 가짐 (Timer Count 값이 동일하지 않음)
  • ContentView와 SecondView가 동일한 인스턴스를 사용하려면 두 번째 화면으로 이동할 때 첫 번째 뷰에 있는 ObservedObject 객체를 SecondView로 넘겨줘야 함

 

 

 

 

네비게이션 추가

  • ContentView (Chap23) 파일 수정
var body: some View {
        NavigationView {
            VStack{
                Text("Timer count = \(timerData.timeCount)")
                    .font(.largeTitle)
                    .fontWeight(.bold)
                    .padding()
                Button(action: resetCount){
                    Text("Reset Count")
                }
                NavigationLink(destination: SecondView(timerData: timerData)){
                    Text("Next Screen")
                }
                .padding()
            }
        }     
    }

 

→ 두 개의 뷰가 동일한 Observable 객체 인스턴스를 구독하는 것을 확인 가능

 

 

 

 

Environment 객체 사용

  • 앱 내에 여러 뷰가 동일한 구독 객체에 접근할 경우 Environment 객체가 더 효과적
  • 두 개의 뷰가 동일한 TimerData 객체에 대한 참조체를 전달하지 않아도 접근 가능
  • ContentView(Chap23) 파일 수정
mport SwiftUI

struct Chap23: View {
    
    @EnvironmentObject var timerData : TimerData 
    
    var body: some View {
        NavigationView {
            VStack{
                Text("Timer count = \(timerData.timeCount)")
                    .font(.largeTitle)
                    .fontWeight(.bold)
                    .padding()
                Button(action: resetCount){
                    Text("Reset Count")
                }
                NavigationLink(destination: SecondView()){
                    Text("Next Screen")
                }
                .padding()
            }
        }     
    }
    
    func resetCount(){
        timerData.resetCount()
    }
}

#Preview {
    Chap23().environmentObject(TimerData())
}

 

 

 

 

 

  • SecondView 파일 수정
import SwiftUI

struct SecondView: View {
    
    @EnvironmentObject var timerData : TimerData
    
    var body: some View {
        VStack{
            Text("Second View")
                .font(.largeTitle)
            Text("Timer Count = \(timerData.timeCount)")
                .font(.headline)
        }
        .padding()
    }
}

#Preview {
    SecondView().environmentObject(TimerData())
}

 

 

 

 

  • SceneDelegate.swift 파일 수정 (루트 화면이 생성될 때 TimerData 객체가 Environment 객체 추가)
  • iOS 15 이상부터는 사용 x
func scene(_ scene: UIScene, willConnectTo session : UISceneSession, 
						options connectionOptions : UIScene.ConnectionOptions){
	
		let contentView = ContentView()

		let timerData = TimerData()

		if let windowScene = scne as? UIWindowScene{
			let window = UIWindow(windowScene : windowScene)
			window.rootViewController = UIHostingController(rootView: contentView**.environmentObject(timerData))**
			self.window = window
			window.makeKeyAndVisible()
		}
}