반응형
LinkedIn 개발자로 성장하면서 남긴 발자취들을 확인하실 수 있습니다.
Github WWDC Student Challenge 및 Cherish, Tiramisul 등 개발한 앱들의 코드를 확인하실 수 있습니다.
개인 앱 : Cherish 내 마음을 들여다보는 시간, 체리시는 디자이너와 PM과 함께 진행 중인 1인 개발 프로젝트입니다.
10년 후, 20년 후 나는 어떤 스토리 텔러가 되어 있을지 궁금하다. 내가 만약에 아직 조금 더 탐구하고 싶은 게 있고, 궁금한 게 있다면, 그게 설사 지금 당장의 내 인생에 도움이 안 되는 것 같더라도 경험해보자. 그 경험들을 온전히 즐기며 내 것으로 만들고, 내 일에 녹여내고... 그러다보면 그 점들이 모여 나란 사람을 그려내는 선이 될 테니까.

Recent Posts
Recent Comments
Total
관리 메뉴

꿈꾸는리버리

SwiftUI : StateObject 실험실 본문

오뚝이 개발자/SwiftUI

SwiftUI : StateObject 실험실

rriver2 2022. 6. 2. 21:24
반응형

저번에 stateObject랑 observedObject를 공부하고 나서 실제로 코딩을 하면서 궁금한 점이 생겨났다..! 

그래서 열게된,, 실험실 !!

재밌었고,, 흥미로웠다 ~ ^^ !!!! 

 

나와 같은 고민을 했던, 고민을 할 사람들에게  조금이나마 도움이 되길 바라며 ,,,

피드백과 질문 그리고 문제제기는 언제나 두 팔 벌려 환영이다 ! 🥹✨☺️


1) StateObject의 Viewmodel이 사라지는 시점

2) 그리고 이 부분은 그냥 아 그렇구나 ~ 하면서 넘겼는데 의문이 들어서 실험하게 되었다.

("애플은 Observable Object를 처음 초기화 할 때는 StateObject를 사용해서 View와 별개의 메모리 공간에 데이터를 저장하도록 하고, 이 객체화된 데이터를 넘겨 받을 때에는 @ObservedObject이나 @EnvironmentObject을 이용해서 게층 전달을 하도록 추천한다." )


1) StateObject의 Viewmodel이 사라지는 시점

다음과 같은 이유로 StateObject가 나왔다고 이전 포스팅에서 알아봤었다.


 

검정뷰 안에 두 개의 주황 뷰가 있다.

검정뷰의 button을 누르면 State가 변경된다.

그리고 두 주황 뷰는 각각 observedObect와 StateObject로 값을 받아 사용한다. 

 

이렇게.. observedObect의 viewmodel은 상위 뷰의 State에 의해 body가 redrawn될 때 초기화가 된다.

Test 1 (하단에 코드 있음)

 

 

StateObject는 객체화 시켜 따로 관리를 하기 때문에 상위 뷰가 redrawn이 되더라도 초기화가 되지 않는다는 것을 알아냈는데,, 

이 궁금증이 해결되고 얼마 지나지 않아

그러면 .. StateObject는 언제 초기화되는 거지? 라는 의문을 가지게 되었다.

이때까지만 해도 redrawn 된다는 게 view가 사라졌다가 다시 그려진다는 건데...

흠,,, 그러면 StateObject는 view가 사라져도.. 안 없어지나..? 이런.. 고민을.. 했다...

 


 

그리고 가장 유력한 가설로, 상위 View가 사라질때 같이 사라질거다! 라고 생각을 했다.

왜냐면 상위뷰의 State가 변할때 StateObject를 유지시키더라도 상위뷰가 사라지면 StateObject를 유지할 이유가 없을 거라 예상했기 때문이다..!

 

와하하핫 가설이 맞았다 ~ 

 

실험을 하고 나니까 좀 당연한 결과였다고 생각이 들지만,,,,   뭐 어떤가 ~~ 재밌었으니 되었다 ^^... 

그리고 당연한 말이지만, 상위뷰가 아니더라도 해당 뷰가 사라지면 viewmodel도 없어진다.

 


 

2) 그리고 이 부분을 그냥 그렇구나 ~ 하고 넘겼는데 의문이 들어서 실험하게 되었다.


 

검정 제일 큰 상위 뷰 하단에 주황 뷰랑 초록 뷰가 있고

초록 뷰 안에 노랑 뷰가 있다. ( 화면만 보고도 이해할 수 있도록 바깥에 테두리를 쳐놨다..! 헤헷)

 


검정 뷰에는 State가 있고, 버튼을 누르면 State 값이 변경된다.

struct ExperimentalView: View {
  @State var test1 : Bool = false
  
  var body: some View {
    VStack{
      Text(test1 ? "Test 1-1" : "Test 1")
        .font(.largeTitle)
        .padding()
      
      Text("Button을 누르면 최상위 뷰의 State가 변함")
      Button("Button") {
        self.test1.toggle()
      }
      .padding()
      
      ExtractedView2(AviewModel: TestViewModel(test2: 0))
      
      ExtractedView3(BviewModel: TestViewModel(test2: 0))
    }
    .padding()
    .background(
      Rectangle()
        .stroke(Color.black)
    )
  }
}

주황뷰에는 검정뷰에서 ObservedObject로 받은 A viewModel이 있고, add One 버튼을 누르면 A viewModel의 프로퍼티 값이 바뀐다.

struct ExtractedView2: View {
  
  @ObservedObject var AviewModel : TestViewModel
  
  var body: some View {
    VStack{
      Text("ObservedObject로 AviewModel을 받음")
      HStack{
        Text("\(AviewModel.test2)")
        Button("add one") {
          AviewModel.addOne()
        }
      }
      .padding()
      .background(Color.orange)
      .foregroundColor(Color.white)
      .cornerRadius(10)
    }
    .padding()
    .background(
      Rectangle()
        .stroke(Color.orange)
    )
  }
}

초록뷰에는 검정뷰에서 StateObject로 받은 B viewModel이 있고 B viewModel의 프로퍼티 값을 보여준다.

노랑뷰에서는 초록뷰에서 ObservedObject로 받은 C viewModel이 있고, add One 버튼을 누르면 C viewModel의 프로퍼티 값이 바뀐다.

 이렇게 하면 초록 뷰는 위 애플 님께서 설명하신 대로 Observable Object를 처음 초기화 할 때는 StateObject를 사용해서 View와 별개의 메모리 공간에 데이터를 저장하도록 하고, 이 객체화된 데이터를 넘겨 받을 때에는 @ObservedObject을 사용하게 된다 ... ! ( 두근 )

struct ExtractedView3: View {
  
  @StateObject var BviewModel : TestViewModel
  
  var body: some View {
    VStack{
      Text("StateObject로 BviewModel을 받음")
      Text("\(BviewModel.test2)")
        .padding()
        .foregroundColor(Color.white)
        .background(Color.green)
        .cornerRadius(10)
      HStack{
        ExtractedView4(CviewModel: BviewModel)
      }
    }
    .padding()
    .background(
      Rectangle()
        .stroke(Color.green)
    )
  }
}

struct ExtractedView4: View {
  
  @ObservedObject var CviewModel : TestViewModel
  
  var body: some View {
    VStack{
      Text("ObservedObject로 CviewModel을 받음")
      HStack{
        Text("\(CviewModel.test2)")
        Button("add one") {
          CviewModel.addOne()
        }
      }
      .padding()
      .foregroundColor(Color.white)
      .background(Color.green)
      .cornerRadius(10)
    }
    .padding()
    .background(
      Rectangle()
        .stroke(Color.yellow)
    )
  }
}

 


 

짜란 ~ 이러면 (다행히도) 예상했던대로 초록뷰에서는 검정 뷰의 Button을 눌러

State가 변경되어도 viewmodel의 값이 유지되는 것을 확인할 수 있다!


 NOTE 

포스팅을 하면서는 내가 좀 삽질을 한 게 아닌가...? 하는 마음이 없지 않아 있었는데

생각해보면 오늘 포스팅한 내용이 되게 답답했었던 부분이고, 주위 사람들에게 물어봐도 쉽게 답을 얻을 수 없었던 부분들이었다.

이렇게... 한발짝씩 성장해 가는 거겠지 ...? ^!^ 

진짜.. 아는 만큼 보인다.. 보이는 게 힘이다..! 

큐큐 NC1 때 작품 ~

어제까지 나를 괴롭혔던 것들이 오늘은 좀 작아 보이는... 이런게.. 힘 !

이게 바로 ! 성장의 매력 아니겠는가 !!

헤헤 그리고 이제 진짜 정말루,, 제발,, property wrapper들과 좀 친해진 느낌..? ^^

앞으로도 화이팅이다 !


Test 1 전체 코드

//
//  ExperimentalView.swift
//  studing_swiftUI
//
//  Created by 이가은 on 2022/06/02.
//

import SwiftUI

class TestViewModel1 : ObservableObject{
  @Published var test1 : Int
  
  init(test1 : Int) {
    self.test1 = test1
    print("ObservedObject init")
  }
  
}

class TestViewModel2 : ObservableObject{
  @Published var test2 : Int
  
  init(test2 : Int) {
    self.test2 = test2
    print("StateObject init")
  }
  
}

struct ExperimentalView: View {
  @State var test1 : Bool = false
  
  var body: some View {
    VStack{
      Text(test1 ? "test1" : "test2")
      
      Button("Button") {
        self.test1.toggle()
      }
      .padding()
      
      ExtractedView1(AviewModel: TestViewModel1(test1: 0))
      
      ExtractedView2(AviewModel: TestViewModel2(test2: 0))
      
    }
    .padding()
    .background(
      Rectangle()
        .stroke(Color.black)
    )
  }
}

struct ExtractedView1: View {
  
  @ObservedObject var AviewModel : TestViewModel1
  
  var body: some View {
    VStack{
      Text("ObservedObject")
      Text("\(AviewModel.test1)")
      Button("add one") {
        AviewModel.test1 += 1
      }
    }
    .padding()
    .background(
      Rectangle()
        .stroke(Color.orange)
    )
  }
}

struct ExtractedView2: View {
  
  @StateObject var AviewModel : TestViewModel2
  
  var body: some View {
    VStack{
      Text("StateObject")
      Text("\(AviewModel.test2)")
      Button("add one") {
        AviewModel.test2 += 1
      }
    }
    .padding()
    .background(
      Rectangle()
        .stroke(Color.orange)
    )
  }
}

struct ExperimentalView_Previews: PreviewProvider {
  static var previews: some View {
    ExperimentalView()
  }
}

Test 2 전체 코드

//
//  ExperimentalView.swift
//  studing_swiftUI
//
//  Created by 이가은 on 2022/06/02.
//

import SwiftUI

class TestViewModel1 : ObservableObject{
  @Published var test1 : Int
  
  init(test1 : Int) {
    self.test1 = test1
    print("StateObject init")
  }
}

struct ExperimentalView: View {
  @State var test1 : Bool = false
  
  var body: some View {
    VStack{
      Text(test1 ? "test1" : "test2")
      
      Button("Button") {
        self.test1.toggle()
      }
      .padding()
      
      
      if(test1){
        VStack{
          Text("상위뷰")
            .font(.largeTitle)
          ExtractedView1(AviewModel: TestViewModel1(test1: 0))
        }
        .padding()
        .background(
          Rectangle()
            .stroke(Color.red)
        )
      }
      
    }
    .padding()
    .background(
      Rectangle()
        .stroke(Color.black)
    )
  }
}

struct ExtractedView1: View {
  
  @StateObject var AviewModel : TestViewModel1
  
  var body: some View {
    VStack{
      Text("StateObject")
      Text("\(AviewModel.test1)")
      Button("add one") {
        AviewModel.test1 += 1
      }
    }
    .padding()
    .background(
      Rectangle()
        .stroke(Color.orange)
    )
  }
}


struct ExperimentalView_Previews: PreviewProvider {
  static var previews: some View {
    ExperimentalView()
  }
}

Test 3 전체 코드

//
//  ExperimentalView.swift
//  studing_swiftUI
//
//  Created by 이가은 on 2022/06/02.
//

import SwiftUI

class TestViewModel : ObservableObject{
  @Published var test2 : Int
  
  init(test2 : Int) {
    self.test2 = test2
    print("TestViewModel init")
  }
  
  func addOne(){
    self.test2 += 1
  }
}

struct ExperimentalView: View {
  @State var test1 : Bool = false
  
  var body: some View {
    VStack{
      Text(test1 ? "Test 1-1" : "Test 1")
        .font(.largeTitle)
        .padding()
      
      Text("Button을 누르면 최상위 뷰의 State가 변함")
      Button("Button") {
        self.test1.toggle()
      }
      .padding()
      
      ExtractedView2(AviewModel: TestViewModel(test2: 0))
      
      ExtractedView3(BviewModel: TestViewModel(test2: 0))
    }
    .padding()
    .background(
      Rectangle()
        .stroke(Color.black)
    )
  }
}
struct ExtractedView: View {
  init(){
    print("hi")
  }
  var body: some View {
    HStack{
      Text("hi")
    }
  }
}

struct ExtractedView2: View {
  
  @ObservedObject var AviewModel : TestViewModel
  
  var body: some View {
    VStack{
      Text("ObservedObject로 AviewModel을 받음")
      HStack{
        Text("\(AviewModel.test2)")
        Button("add one") {
          AviewModel.addOne()
        }
      }
      .padding()
      .background(Color.orange)
      .foregroundColor(Color.white)
      .cornerRadius(10)
    }
    .padding()
    .background(
      Rectangle()
        .stroke(Color.orange)
    )
  }
}

struct ExtractedView3: View {
  
  @StateObject var BviewModel : TestViewModel
  
  var body: some View {
    VStack{
      Text("StateObject로 BviewModel을 받음")
      Text("\(BviewModel.test2)")
        .padding()
        .foregroundColor(Color.white)
        .background(Color.green)
        .cornerRadius(10)
      HStack{
        ExtractedView4(CviewModel: BviewModel)
      }
    }
    .padding()
    .background(
      Rectangle()
        .stroke(Color.green)
    )
  }
}

struct ExtractedView4: View {
  
  @ObservedObject var CviewModel : TestViewModel
  
  var body: some View {
    VStack{
      Text("ObservedObject로 CviewModel을 받음")
      HStack{
        Text("\(CviewModel.test2)")
        Button("add one") {
          CviewModel.addOne()
        }
      }
      .padding()
      .foregroundColor(Color.white)
      .background(Color.green)
      .cornerRadius(10)
    }
    .padding()
    .background(
      Rectangle()
        .stroke(Color.yellow)
    )
  }
}

struct ExperimentalView_Previews: PreviewProvider {
  static var previews: some View {
    ExperimentalView()
  }
}
반응형
Comments