iOS SwiftUi ViewModel
포스트
취소

iOS SwiftUi ViewModel

안녕하세요. Narvis2 입니다.
이번 시간에는 Swift 에서 ViewModel 을 사용하여 MVVM 패턴을 사용하는 방법에 대하여 알아보도록 하겠습니다.

🍀 MVVM (Model - View - ViewModel)


  • Model 👉 Application 에서 사용되는 데이터와 그 데이터를 처리하는 부분
    • 서버에서 들어오는 데이터, 디바이스에 저장되는 Local 데이터
  • View 👉 사용자에게 보여지는 UI 부분
    • ViewModel의 데이터가 변경된 것을 감지해 UIUpdate
  • ViewModel 👉 View를 표현하기 위해 만든 View를 위한 Model
    • ViewModel 을 연결 시켜 주는 곳
    • 데이터의 변경사항을 알려주는 LiveData를 가지고 있음
    • Model 이 변경된 것을 감지하여 UI를 위한 데이터를 변경해야함
  • ViewModel을 모르지만 ViewModel을 알고 있음
  • ViewModelView를 모르지만 Model을 알고 있음
  • View를 통해 들어온 사용자 인터렉션은 ViewModel에게 전달되어 특정 로직이 실행됨
  • ✅ 즉, 의존성 분리
    • ViewModel 사이의 의존성이 없음 (View의 부담을 줄여줌)
    • ViewModelData Binding을 통해 ViewModel을 이어줌
    • 각각의 부분은 독립적 이기 때문에 모듈화 하여 개발할 수 있음.
  • ✅ 장점
    • Viewdata 가 완전 분리된다.
    • Viewdata관리를 할 필요가 없으므로 UI update에만 집중할 수 있음
    • View 간에 data 공유가 훨씬 쉬워진다.
    • 유지보수의 장점 👉 의존성이 분리되므로, View의 코드를 변경할 때 다른 부분의 코드를 변경할 필요가 없음
  • ✅ 동작 순서
    • 1️⃣ 사용자의 actionView를 통해 들어옴
    • 2️⃣ command 패턴을 사용하여 ViewModelAction을 전달함
    • 3️⃣ ViewModelModel에서 data를 요청하고, ModelViewModel에서 요청받은 데이터를 ViewModel에 전달함
    • 4️⃣ ViewModel은 응답받은 데이터를 가공, 저장함
    • 5️⃣ ViewViewModel과의 Data Binding을 이용해 화면을 갱신함 (Observer 패턴)

🍀 Swift with ViewModel


  • ObservableObject를 이용하여 Data ModelView를 쉽게 Binding할 수 있음
  • ObservableObjectViewModel을 설정하고, View 단에서는 ViewModel@ObservedObject를 붙여줌으로서 ViewModel변화를 관찰 할 수 있고, 그 변화에 반응할 수 있음

☘️ @ObservableOjbect

  • 해당 클래스의 인스턴스를 관찰하고 있다가 값이 변경되면 ViewUpdate한다.

    예제 👇

    1
    2
    3
    
    class MainviewModel: ObservableObject {
    
    }
    

☘️ @Published

  • ObservableOject 에서 속성을 선언할 때 사용 하는 PropertyWrapper
  • 해당 속성이 업데이트 될 때마다 ViewUpdate, $ operator를 붙여 게시자에 엑세스 가능
  • @Published가 붙어있는 변수가 변경 되면 이를 지켜보고 있는 View에게 ViewModel이 변경되었음을 알려주고, View는 새로운 객체를 바탕으로 ViewRefresh 한다.
  • Android 의 LiveData와 유사

    예제 👇

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    
    class MainviewModel: ObservableObject {
      @Published var youngjun = Person(name: "영준", birthday: Date())
    
      var name: String {
          youngjun.name
      }
    
      var age: String {
          return "29"
      }
    
      func changeName(_ name: String) {
          youngjun.name = name
      }
    }
    

☘️ @ObservedObject

  • ObservableObject구독하고 값이 Update될 때 마다 View를 갱신하는 PropertyWrapper
  • View를 만료시키고 새로 그림

    ⚠️ View가 새로 그려질 때 마다 인스턴스가 새로 초기회됨

    예제 👇

    1
    2
    3
    
    struct ContentView: View {
      @ObservedObject var viewModel = MainViewModel()
    }
    

☘️ @StateObject

  • 단 한 번 인스턴스가 생성
  • View를 처음부터 새로 그리지 않고, ObservableObject에서의 데이터가 변할 때, 그 ObservableObject의 데이터가 들어간 부분만 View를 다시 그 림
  • View가 얼마나 다시 그려지든 상관없이 별개의 객체로 관리

    예제 👇

    1
    2
    3
    
    struct ContentView: View {
      @StateObject var viewModel = MainViewModel()
    }
    

☘️ SharedViewModel 구현

  • ✅ 추천
    • Observable Object처음 초기화 할 때는 StateObject 를 사용하고, 이미 객체화된 것을 넘겨 받을 때는 ObservedObject를 사용
  • StateObjectObservedObject를 사용하여 SharedViewModel을 사용가능
  • 상위 View에서 객체로 만들어서 따로 저장해두고, 하위 View도 이 Observable Object의 변화를 감지하고, 같은 정보에 접근 할 수 있도록 할 수 있음

    예제 👇

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    
    struct UpperView: View {
      @StateObject var viewModel: MainViewModel = MainViewModel()
      var body: some View {
          LowerView(viewModel: viewModel)
      }
    }
    
    struct LowerView: View {
      @ObservedObject var viewModel: ViewModel
      var body: some View {
          Text("Hello")
      }
    }
    

☘️ @State

  • DataBinding을 위해 사용
    • $ 키워드를 붙혀 DataBinding
    • $가 붙으면 값을 수정가능한 Binding타입 참조
  • @StateView 외부로는 사용할 수가 없고 private 형태로 내부에서만 사용

    예제 👇

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    
    struct ContentView: View {
      @State var isToggleOn: Bool = true
    
      var body: some View {
        VStrck {
          Toggle(isOn: $isToggleOn) {
            Text("글자를 가립니다.")
          }.padding()
    
          if isToggleOn {
            Text("그으으을자!")
          }
        }
      }
    }
    

☘️ @Binding

  • 2개의 View가 동시에 하나의 State를 참조 해야하는 경우 사용

    예제 👇

    1
    2
    3
    4
    5
    6
    7
    
    struct Toggole: View {
      @Binding var isOn: Bool
    
      var body: some View {
        // TODO..
      }
    }
    
이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.