SwiftUI의 데이터 흐름

  • SwiftUI는 Apple 앱을 개발하는 완전히 새로운 방식입니다.
  • 이번에는 SwiftUI에서의 데이터 흐름과 라이프사이클을 알아보고자 합니다.
  • SwiftUI 데이터 흐름에 대해서는 애플의 WWDC19WWDC20 영상이 있습니다.
  • SwiftUI 데이터 흐름에 대한 애플의 공식 문서도 존재합니다.

SwiftUI 라이프사이클

  • SwiftUI에는 View의 상태를 나타내는 함수가 즉, 라이프사이클이 아래와 같이 단 두가지 밖에 없습니다.
  • 대신 상태를 나타내는 다양한 Property Wrapper가 존재해 Data 흐름에 대한 여러 상태에 대응할 수 있습니다.
.onAppear {
    print("View appeared")
}

.onDisappear {
    print("View disappeared")
}

@State

  • 일반적으로 struct는 값 타입이여서 struct내의 값을 변경할 수 없습니다.
  • SwiftUI는 @State를 제공해 struct내의 값을 변경할 수 있게 해줍니다.
struct ContentView: View {
  @State private var number = 0
}
  • SwiftUI의 view는 struct이고, 이는 언제든 소멸되거나 재생성됩니다.
  • 그렇기 때문에 @State를 사용해 지속적으로 변형 가능한 변수를 만드는 것입니다.
  • 단, @State는 String, Int, Bool과 같은 간단한 타입에만 사용되는 것이 좋습니다.
  • 일반적으로 @State 변수는 private으로 선언되고, 다른 view와 공유되지 않습니다.
  • 다른 view와 값을 공유하고 싶다면, @StateObject나 @ObservedObject를 사용하면 됩니다.

@Binding

  • @Binding은 부모 view의 @State와 같은 값을 양방향으로 연결되도록 해줍니다.
  • 아래 코드에서 isPresentedshowAddView를 바인딩 시켜줘서 값을 변경해줍니다.
struct ContentView: View {
  @State private var showAddView = false

  var body: some View {
    VStack {
      Text("Hello World.")
    }
  }
  .sheet(isPresented: $showAddView) {
    AddView(isPresented: self.$showAddView)
  }
}

struct AddView: View {
  @Binding var isPresented: Bool

  var body: some View {
    Button("Dismiss") {
      self.isPresented = false
    }
  }
}

ObservableObject

  • ObservableObject는 Protocol으로 Combine 프레임워크의 일부입니다.
  • 이것을 사용하기 위해서는, Protocol을 준수하고 @Published를 사용하면 됩니다.
  • @Published를 사용하면 변수의 값이 추가되거나 삭제 되었다는 것을 View가 알 수 있게 해줍니다.
  • ObservableObject는 MVVM 아키텍쳐의 ViewModel에 적용하기 좋은 프로토콜입니다.
class MyViewModel: ObservableObject {
  @Published var dataSource: MyModel
  
  init(dataSource: MyModel) {
    self.dataSource = dataSource
  }
}

@StateObject

  • WWDC 2020에서 애플은 @StateObject를 추가로 공개했습니다.
  • @ObservedObject와 거의 같은 방식으로 작동하는데요.
  • SwiftUI가 View를 다시 랜더링 할 때, 실수록 취소되는 것을 방지해줍니다.
struct ContentView: View {
  @StateObject var user = User()
}

@ObservedObject

  • SwiftUI는 @ObservedObject를 통해 view가 외부 객체를 감지하게 해줍니다.
  • 아래 코드에서 User class는 ObservableObject를 준수하고 @Published 변수를 갖고 있습니다.
  • @ObservedObject user 변수는 이러한 User class 객체를 담고 있습니다.
  • SwiftUI는 이러한 user 객체의 @Published 변수 값이 변경될 때 view를 refresh합니다.
class User: ObservableObject {
  @Published var name = "Hohyeon Moon"
}

struct ContentView: View {
  @ObservedObject var user = User()

  var body: some View {
    VStack {
      Text("Your name is \(user.name).")
    }
  }
}

@EnvironmentObject

  • @EnvironmentObject는 보통 앱 전반에 걸쳐 공유되는 데이터에 사용됩니다.
  • @EnvironmentObject.environmentObject()를 통해 값을 전달할 수 있습니다.
  • 전달하는 object는 ObservableObject 프로토콜을 준수해야 합니다.
  • 아래 코드와 같이 root view를 제공하면, 어떠한 view에서도 사용이 가능합니다.
// MySettings.swift
class Settings: ObservableObject {
  @Published var version = 0
}

// SceneDelegate.swift
var settings = UserSettings() 
window.rootViewController = UIHostingController(rootView: ContentView().environmentObject(settings))

// MyViews.swift
struct ContentView: View {
  @EnvironmentObject var settings: UserSettings

  var body: some View {
    NavigationView {
      VStack {
        Button(action: {
          self.settings.version += 1
        }) {
          Text("Increase version")
        }

        NavigationLink(destination: DetailView()) {
          Text("Show Detail View")
        }
      }
    }
  }
}

struct DetailView: View {
  @EnvironmentObject var settings: UserSettings

  var body: some View {
    Text("Version: \(settings.version)")
  }
}

총 정리

swiftui-data-flow

마무리

  • 이렇게 해서 SwiftUI에서는 라이프사이클과 데이터 흐름을 어떻게 처리하는지 알아봤습니다.