λ³Έλ¬Έ λ°”λ‘œκ°€κΈ°
🍎 iOS/WWDC

[WWDC19] Data Flow Through SwiftUI - Part 2

by Danna 2022. 8. 30.
728x90
728x90

Working With External Data

πŸ’¬ Part1 μ—μ„œλŠ” ...

  • μœ μ €μ™€ SwiftUI λ·° κ°„ 데이터 흐름에 λŒ€ν•΄ μ–˜κΈ°ν•΄λ΄€μ—ˆλ‹€.
  • @State ν”„λ‘œνΌν‹° 래퍼λ₯Ό 톡해 값이 변경됨을 νŠΈλž˜ν‚Ήν•˜λŠ” λ³€μˆ˜λ₯Ό λ‘˜ 수 있고,
  • @Binding μ„ 톡해 μžμ‹λ·°μ—μ„œλ„ 같은 데이터λ₯Ό 바라보도둝 ν•  수 μžˆμ—ˆλ‹€.

πŸ™‹‍♀️ Part2 μ—μ„œλŠ”?

  • λ·° λ‚΄λΆ€μ—μ„œμ˜ λ™μž‘μ„ κ΄€λ¦¬ν•˜λŠ” @State λ³€μˆ˜λ‘œλŠ” λΆˆκ°€λŠ₯ν•œ μ™ΈλΆ€μ—μ„œ λ°œμƒν•˜λŠ” 이벀트, μ™ΈλΆ€μ—μ„œ κ΄€λ¦¬λ˜λŠ” λ°μ΄ν„°λŠ” μ–΄λ–»κ²Œ μ²˜λ¦¬ν•΄μ•Όν• μ§€ 닀뀄본닀.
  • 결둠적으둜 μœ μ € μΈν„°λ ‰μ…˜μ΄ μ•„λ‹Œ timer, notification 처럼 μ™ΈλΆ€μ—μ„œ λ°œμƒν•˜λŠ” μ΄λ²€νŠΈλ„ λ™μΌν•œ νλ¦„μœΌλ‘œ λ™μž‘ν•œλ‹€.
    • action 생성 -> State κ°’ λ³€κ²½ -> 뷰에 λŒ€ν•œ μƒˆλ‘œμš΄ μΉ΄ν”Ό -> μœ μ €μ—κ²Œ μƒˆλ‘­κ²Œ λ³΄μ—¬μ€Œ
  • SwiftUI μ—μ„œλŠ” μš”λŸ¬ν•œ μ§€μ†μ μœΌλ‘œ 값이 λ³€κ²½λ˜λŠ” μ™ΈλΆ€ 이벀트λ₯Ό λ‚˜νƒ€λ‚΄κΈ° μœ„ν•œ 좔상화 νƒ€μž…μΈ Publisher κ°€ μžˆλ‹€.

 

Combine Publisher

Combine 은 비동기 이벀트λ₯Ό ν•Έλ“€λ§ν•˜κΈ°μœ„ν•œ ν”„λ ˆμž„μ›Œν¬μ΄κ³ , Publisher λŠ” Combine ν”„λ ˆμž„μ›Œν¬μ—μ„œ μ†Œκ°œλœ νƒ€μž…μ΄λ‹€.

  • Publisher λŠ” μ‹œκ°„μ΄ 지남에 따라 값을 보낼 수 μžˆλŠ” νƒ€μž…μœΌλ‘œ, RxSwift 의 Observable λŠ” λΉ„μŠ·ν•œ κ°œλ…μ΄λ‹€.
  • SwiftUI λ·°μ—μ„œ Publisher 의 값을 κ΅¬λ…ν•΄μ„œ μ‚¬μš©ν•˜λ €λ©΄ main thread μ—μ„œ 값을 λ°©μΆœν•΄μ•Όν•˜κ³ , μŠ€μΌ€μ₯΄λŸ¬ 지정을 μœ„ν•œ .receive(on:) λ©”μ†Œλ“œλ₯Ό μ œκ³΅ν•œλ‹€.

πŸ’‘ Publisher (Protocol)
Declares that a type can transmit a sequence of values over time.

πŸ’‘ Combine
Customize handling of asynchronous events by combining event-processing operators.

func receive<S>(
    on scheduler: S,
    options: S.SchedulerOptions? = nil
) -> Publishers.ReceiveOn<Self, S> where S : Scheduler

 

⌚️πŸ₯‘ example

podcast player 에 μž¬μƒμ‹œκ°„μ„ ν‘œμ‹œν•˜λŠ” λ·°λ₯Ό μΆ”κ°€ν•΄λ³΄μž.

  • @State λ³€μˆ˜μΈ currentTime 을 μΆ”κ°€
  • currentTime 을 ν‘œμ‹œν•΄μ€„ Text λ·°λ₯Ό μΆ”κ°€
  • 뷰에 λŒ€ν•΄μ„œ onReceive λ©”μ†Œλ“œλ₯Ό μΆ”κ°€
    • λ©”μ†Œλ“œμ˜ 인자둜 μ§€μ •ν•œ publisher κ°€ 값을 λ°©μΆœν•  λ•Œ λ§ˆλ‹€ closure κ°€ μˆ˜ν–‰λœλ‹€.
  • 이제 SwiftUI 와 Publisher κ°„ λ””νŽœλ˜μ‹œκ°€ μƒκΈ°κ²Œ λ˜μ—ˆλ‹€!
    • currentTimePublisher κ°’ λ³€κ²½ > state λ³€μˆ˜ κ°’ λ³€κ²½ > view μƒˆλ‘œ 그렀짐

πŸ’‘onReceive(_:perfome:)
Adds an action to perform when this view detects data emitted by the given publisher.

func onReceive<P>(
    _ publisher: P,
    perform action: @escaping (P.Output) -> Void
) -> some View where P : Publisher, P.Failure == Never

 

BindableObject ObservableObject Protocol

⚠️ BindableObject is replaced by the ObservableObject protocol from the Combine framework. (50800624)


2019 WWDC μ˜ˆμ œμ™€ 변경사항이 μ’€ μžˆλ„€μš” πŸ₯Ί

  • BindableObject -> ObservableObject 둜 변경됨
  • didChange λ₯Ό ν•„μˆ˜λ‘œ κ΅¬ν˜„ν•΄μ•Όν•¨ -> willChange λ₯Ό ν•„μˆ˜λ‘œ κ΅¬ν˜„ν•΄μ•Όν•¨
    • 직접 κ΅¬ν˜„ν•˜μ§€ μ•Šμ•„λ„ @Published λ₯Ό 뢙인 ν”„λ‘œνΌν‹°λŠ” 값이 λ³€κ²½λ˜κΈ°μ „μ— μžλ™μœΌλ‘œ 값을 λ°©μΆœν•¨
    • λ·°μ—μ„œ ν•΄λ‹Ή λͺ¨λΈμ—μ„œ μ‚¬μš©ν•  ν”„λ‘œνΌν‹°μ— λŒ€ν•΄μ„œλ§Œ @Published λ₯Ό λΆ™μ—¬μ£Όλ©΄ 됨.
class Contact: ObservableObject {
    @Published var name: String
    @Published var age: Int

    init(name: String, age: Int) {
        self.name = name
        self.age = age
    }

    func haveBirthday() -> Int {
        age += 1
        return age
    }
}

let john = Contact(name: "John Appleseed", age: 24)
cancellable = john.objectWillChange
    .sink { _ in
        print("\(john.age) will change")
}
print(john.haveBirthday())
// Prints "24 will change"
// Prints "25"

 

Creating Dependencies on ObservableObject

SwiftUI λ·°μ—μ„œ ObservableObject 둜 μ„€μ •λœ λͺ¨λΈμ— λŒ€ν•œ λ””νŽœλ˜μ‹œλ₯Ό μ—°κ²°ν•˜λŠ” 방법

  • λ·°μ—μ„œ @ObservedObject ν”„λ‘œνΌν‹° 래퍼λ₯Ό 톡해 ν•΄λ‹Ή λͺ¨λΈ νƒ€μž…μ˜ λ³€μˆ˜λ₯Ό μ„ μ–Έν•œλ‹€.
  • λ·°λ₯Ό μ΄ˆκΈ°ν™”ν• λ•Œ model instance λ₯Ό λ„˜κ²¨μ€€λ‹€.
  • ObservableObject λͺ¨λΈμ— λŒ€ν•œ subscribe λ₯Ό μžλ™μœΌλ‘œ ν•˜λŠ”κ²ƒ!
  • μ•„λž˜ μ˜ˆμ œμ—μ„œ λ³€κ²½λœ 점
    • BindableObject → ObservableObject
    • @ObjectBinding  @ObservedObject

Creating Dependencies Indirectly

뷰의 μžμ‹λ·°μ—μ„œλ§Œ ν•„μš”λ‘œν•˜λŠ” λ°μ΄ν„°μ˜ λ””νŽœλ˜μ‹œλ₯Ό μ—°κ²°ν•˜λŠ” 방법

  • @EnvironmentObject ν”„λ‘œνΌν‹° 래퍼λ₯Ό 톡해 계측을 μ „λΆ€ κ±°μΉ˜μ§€ μ•Šκ³ , ν•˜μœ„ λ·°μ—μ„œ λͺ¨λΈμ— λŒ€ν•œ μ˜μ‘΄μ„±μ„ κ°–κ²Œν•  수 μžˆλ‹€.
  • μ—¬λŸ¬κ°œμ˜ λ·°μ—μ„œ 같은 λͺ¨λΈμ„ μ‚¬μš©ν•  μˆ˜λ„ μžˆλ‹€. 데이터가 변경될 λ•Œ μ—¬λŸ¬κ°œμ˜ λ·°μ—μ„œ μžλ™μœΌλ‘œ μ—…λ°μ΄νŠΈκ°€ λœλ‹€.
    • μ§€λ‚œλ²ˆμ— μ–ΈκΈ‰λœ source of truth κ°€ μƒκ°λ‚˜λŠ” 것 πŸ‘

πŸ’‘EnvironmentObject
A property wrapper type for an observable object supplied by a parent or ancestor view.

πŸ’‘environmentObject(_:)
Supplies an ObservableObject to a view subhierarchy.

 

PlayerView()
    .environmentObject(player)

 

ObservedObject vs EnvironmentObject

μ΄ˆλ‘μƒ‰ λ·°μ—μ„œλ§Œ Model 을 μ‚¬μš©ν•΄μ•Όν•˜λŠ” 경우

  • @ObservedObject λ₯Ό μ΄μš©ν•˜λ©΄ λ·°κ³„μΈ΅μ—μ„œ μžμ‹λ·°λ‘œ λ‚΄λ €κ°€λ©΄μ„œ λͺ¨λΈμ„ 계속 μ „λ‹¬ν•΄μ•Όν•œλ‹€.
  • @EnvironmentObject λ₯Ό μ‚¬μš©ν•˜κ²Œλ˜λ©΄ ν•„μš”ν•œ λ·°μ—λ§Œ λͺ¨λΈμ„ μ „λ‹¬ν•˜λ©΄ λœλ‹€.

 

Sources of Truth

SwiftUI λŠ” 두가지 λ°©λ²•μœΌλ‘œ sources of truth λ₯Ό μ œκ³΅ν•œλ‹€.

  • state : view μ—μ„œλ§Œ μ‚¬μš©ν•˜λŠ” value νƒ€μž…, λ©”λͺ¨λ¦¬μ— ν• λ‹Ήλ˜κ³  μƒμ„±λ˜λŠ”κ±Έ ν”„λ ˆμž„μ›Œν¬μ—μ„œ κ΄€λ¦¬ν•œλ‹€.
  • ObservedObject : view μ™ΈλΆ€μ˜ 데이터, DB λͺ¨λΈ λ“±,, κ°œλ°œμžμ— μ˜ν•΄ κ΄€λ¦¬λœλ‹€.

 

sources of truth κ°€ 보μž₯λ˜κ³ λ‚˜λ©΄, reusable component 에 집쀑할 수 있게 λœλ‹€.

  • λ·° λ‚΄μ—μ„œ 직접 데이터λ₯Ό λ³€κ²½ν•˜λŠ” 일이 적어지고, read-only 데이터λ₯Ό μ„ ν˜Έν•˜κ²Œ 될 것이닀.
  • SwiftUI λ‚΄μ—μ„œ 일반적인 데이터와 Environment λŠ” read-only

 

λ§Œμ•½ κ°’ 변경이 ν•„μš”ν•œ κ²½μš°μ—λŠ” Binding 을 μ‚¬μš©ν•˜λ©΄ λœλ‹€.

  • State 뿐 μ•„λ‹ˆλΌ, ObservedObject, Binding λ³€μˆ˜μ— λŒ€ν•΄μ„œλ„ 바인딩이 κ°€λŠ₯ν•˜λ‹€.

 

πŸ†š State vs ObservedObject

  • State property λŠ” λ·° λ‚΄μ—μ„œλ§Œ μ‚¬μš©λ˜λŠ”κ±°λΌ, ν”„λ‘œν† νƒ€μ΄ν•‘μ— μ ν•©ν•˜λ‹€.
  • 일반적으둜 λ°μ΄ν„°λŠ” DB λ“± SwiftUI λ·° μ™ΈλΆ€μ—μ„œ κ΄€λ¦¬λ˜κΈ° λ•Œλ¬Έμ— ObservableObject ν”„λ‘œν† μ½œμ„ μ±„νƒν•˜λŠ” 객체둜 κ΄€λ¦¬λ˜λŠ” 것이 μ’‹λ‹€.

 

State 쓰기전에 μƒκ°ν•΄λ³΄μž πŸ€” μ •λ§ μ§„μ§œ λ¦¬μ–Όλ‘œ λ·°μ—μ„œλ§Œ 관리될 데이터인가?

  • Button 처럼 μ‚¬μš©μžκ°€ λˆŒλ €μ„λ•Œ ν•˜μ΄λΌμ΄νŠΈ 컬러λ₯Ό μ„€μ •ν•˜λŠ” λ“± 정말 λ²„νŠΌμ΄λΌλŠ” λ·°μ—μ„œ κ΄€λ¦¬λ˜μ–΄μ•Όν•˜λŠ” λ°μ΄ν„°λŠ” State κ°€ μ μ ˆν•˜λ‹€.
  • ν•˜μ§€λ§Œ 그게 μ•„λ‹ˆλΌλ©΄, ObservableObject 처럼 SwiftUI μ—μ„œ μ œκ³΅λ˜λŠ” λ‹€λ₯Έ 방법을 νƒν•˜μž.

 

μš”κ²ƒμ΄ SwiftUI 둜 μž¬μ‚¬μš©κ°€λŠ₯ν•œ λ·°λ₯Ό λ§Œλ“œλŠ” 방법이고, λͺ¨λ“  μ†Œν”„νŠΈμ›¨μ–΄μ—μ„œ 일반적으둜 μ μš©λ˜λŠ” 방법이닀.

  • λͺ¨λ“  μ†Œν”„νŠΈμ›¨μ–΄μ—λŠ” 데이터가 있고, 데이터에 μ•‘μ„ΈμŠ€ ν•  수 μžˆλ‹€.
  • λ°μ΄ν„°μ˜ μ›μ²œμ„ μ΅œμ†Œν™”ν•˜κ³ , μž¬μ‚¬μš©κ°€λŠ₯ν•œ μ»΄ν¬λ„ŒνŠΈλ₯Ό λ§Œλ“¬μœΌλ‘œμ¨ μ•±μ˜ 전체적인 버그λ₯Ό μ œκ±°ν•  수 μžˆλ‹€.
  • SwiftUI λ₯Ό μ‚¬μš©ν•œλ‹€λ©΄ 이런 κ°œλ…μ„ μ μš©ν•˜λŠ”κ²Œ 맀우 μ‰¬μ›Œμ§„λ‹€. πŸ˜‰

 

728x90
728x90