본문 바로가기
WWDC

WWDC 20 - Data Essentials in SwiftUI

by vapor3965 2021. 6. 17.

목차


    • 세션보면서 정리한 내용입니다. 해석이 잘못된 경우가 있을수있으니 발견하시면 댓글로 남겨주시면 감사하겠습니다🙏🏻

     

     

    관련내용 

     

    • 아래의 차이점을 분명히알려주도록한다.

     

     

    요약

    • SwiftUI를 사용할경우, 3가지를고려해야한다.
      • 뷰가 필요한데이터는 무엇인지,
      • 데이터를 조작할것인지,
      • 데이터는 어디에서오는지,
    • source of truth
      • 데이터의원천?
    • @State
      • 매우가볍게, 상태와 뷰를 업데이트하게해준다.
        • 뷰처럼 일시적으로존재하는경우에 가볍게사용한다.
      • 다른뷰에게 전달하기위해서는 Binding이 필요하다 또한 전달할때는 $ 를이용한다.
      • SwiftUI가 자동적으로 옵저빙하여 뷰를 업데이트해준다.
        • 👍하지만!! value타입인경우에만 view가업데이트된다.
        • 왜냐하면, view는 @State의 상태가변해야 업데이트하는데, class는 참조타입이므로, 값이변한지모르기때문!!
    • ObservableObject protocol - AnyObject
      • 클래스만 채택할 수 있다.
      • 값을 언제든지 옵저빙할수있도록 도와준다! 
      • 내부적으로 Publisher를 갖는다.
      • Binding을 지원한다 - $ 를 이용한다. 
      •  
      • @StateObject
        • 인스턴스를소유한다.
        • body가 작동하기전에 적절한타임에 인스턴화된다.
        • View와 라이프사이클이다르다. View가사라져도 남아있는다. 참조하기때문.
      • @ObservedObject
        • 인스턴스를소유하지않는다. ?
        • 즉, 수퍼뷰에서 전달받아지도록한다. ( 수퍼뷰는 @StateObjectd로 선언되어있고, )
        • 👍View와 라이프사이클이같다. View가 다시그려지면, ObservedObject도 초기화된다. ( 물론 수퍼뷰로부터 StateObjecte로 전달받은경우는 안그럼. )
      • @EnvironmentObject
        • 멀리있는 뷰에게 바로전달할수있다.
        • ObservedObject로 매번 전달하면 무거울수있다. ( 힙할당? )
    • 👍view는 매우가볍고, 이벤트 -> source of truth 변형 -> 새로운view 카피생성 이된다. ( 핵심 )
      • 👍즉, 상태에의해 계속 뷰가 업데이트되는데, 업데이트가아닌, 새로운뷰를계속생성하느것이다.
      • 그러므로, 뷰안에서 매번객체( 특히 힙할당) 를 생성하지않도록해야한다! ( 특히나 메인큐에서진행하니까…. 다른 비동기…는 음 백그라운드에서해야겠지? )

     

    🔥@ObservedObject와, @StateObject차이는?

    • A View에 ObservedObject로 생성했을때,
    • A View를 갖는 부모뷰가 State에의해 변경된다면, 자식뷰들도 다시그려지게되는데, 이때, A View도 다시그려지므로, ObservedObject도 다시초기화된다.

     

     

     

     

     

     


    👍SwiftUI를로 시작한다면 3가지를 고려해야한다.

    1. 뷰는 자신의일을 하기위해 어떤 데이터가필요할까?
      • 여기서는 썸네일, 타이틀, 퍼센티지
    2. 뷰는 어떻게 데이터를조작할까?
      • 여기서는 데이터가변하지않는다.
    3. 👍데이터는 어디에서오는걸까? ( 가장중요!

    우선 뷰는 자신의일을하기위해 어떤데이터가필요할까?

    • 책이름, 책이미지, 저자가 필요하다
    • 진행상태를나타내는 progress가필요하다

    뷰는 데이터를 어떻게 조작할까?

    • 데이터는변하지않기때문에, let으로 선언한다.

    데이터는어디에서올까?

    • BookCard가 초기화될때, superview에서 전달되어진다.
    • 즉, source of truth 는 좀 더 높은 상위계층에있을것이다.
    • BookCard는 superview의 body가 실행될때마다 초기화된다.

    BookCard를 다이어그램으로표현하면아래와같다.
    왼쪽은 SwiftUI의 구성이고,
    오른쪽은 뷰에 사용되는 데이터이다.

    그리고 나는 더 특별한걸만들고싶다.
    프로그래스바를 누르면, 세부정보를확인할수있는 뷰와,

    업데이트할수있는 시트뷰를 추가하고싶다.
    Update progress를 누르면 업데이트하도록한다.

    뷰와 시트뷰의 상호관계를 살펴보자.
    우선, 뷰에서 Update progress버튼을 누르는 것과 메소드를 준비한다.

    그리고, 이러한 뷰들이 작동하기위해 필요한 데이터를 정의한다.

    이러한프로퍼티들을 캡슐화하는것이좋다.

    캡슐화하면, 가독성이좋아지고,
    독립적으로 테스트할수있다.

    • 또한 값타입으로…..

    👍이번에는 뷰가 데이터를조작해야하므로,
    또한 데이터는 캡슐화되어있기때문에, 머, 아래와같이 조작하도록한다.

     

    👍그럼데이터는 어디에서올까?

    우선, Editor Config는 이뷰에만있는 지역적이다.
    전달해주는 parent view가 없다.
    그러므로, local source of truth 로만들어줄필요가있다.

    SwiftUI에서, 가장 간단한 source of truth는 State이다.

    다이어그램으로보자.
    사용된데이터는 이번에는 굵게 테두리를 표현했다.
    이는 우리ㅐ신 SwiftUI가 조작되어진다는것을 의미한다.
    이게왜중요하냐면, 이 뷰는 일시적으로존재하기때문에, 렌더링이끝난이후에는 struct가사라지기때문? ( 아래와같이 )

    이렇게 사용되고나면 BookView struct는 사라진다!
    👍하지만, SwiftUI는 State로 정의한프로퍼티는 유지시킨다!!!

    👍그래서, 나중에 다시 View를 렌더링하면, 다시 뷰를만들지만, State - EditorConfig는 사라지지않았기때문에 그냥연결시켜준다.!

    하단에 조작할수있는 Editor뷰는
    EditorConfig로부터오는 데이터가필요하다.
    또한 조작하기때문에 var로 선언한다.

    자, 하지만, 데이터가 BookView로부터 전달되어야할까?
    값타입이기때문에 전달한다고해도 변경이되면 원본에는 반영이안되기때문에 안된다!!

    그럼, state로 명시해서 전달하면?

    • 그것도안된다!
    • State는 새로운 source of truth를 만들어낸다.
    • 그러므로 변경은 그자체의 state에게만 영향을주고, 공유되어진놈한테는 영향이없다!

     

     

     

    즉, 그러므로 우리는 source of truth에대한 쓰기접근을 BookViwe와 ProgressEditor에서도 공유하고싶다

    👍SwiftUI에서는 어더한 source of truth 든지간에 쓰기접근을 공유하도록 하는것은 Binding이다.

    read-write는 기존의존재하는데이터에 참조하고, 공유되어진다!
    그러므로 Binding으로선언하여 전달하면,
    Binding의 변수가변경이되면 전달한다.

    그러므로, @Binding으로선언하고
    BookVIew에서는 $ 달러사인으로 전달한다.
    $달러사인으로해야 State로부터 Binding을 생성한다.

    • 👍왜냐하면, State의 프로퍼티뤠퍼의 projected value는 BInding이므로!!!!

    👍SwiftUI에서는 Binding을 많이사용한다

    • 아래와같이 TextEditor는 Binding의 Binding을 사용한다.
    • 즉, EditorCnfig안에서 note프로퍼티를 Binding한것!!
    • 즉 기존의Binding에서 새로운 Binding을사용하게해준다.
    • 그러므로, Binding은 State에서만가능한것은아니다!!!!

     

    어떤데이터가필요할까,
    데이터는조작될까,
    데이터는어디에서오는가,

    @State를, 뷰에의해 일시적으로 존재하는데이터를위해사용하라 ( 계속남아잇도록하기위해? )

    @BInding은 다른뷰에소유된 변하는데이터를위해사용하라.

     

     

     

    SwiftUI는 State, Binding뿐만아니라 다른것도많다.

    👍🔥ObservableObject

    Persist, Sync뿐만아니라, 데이터라이프라사이클관리함.

    클래스만사용가능한 protocol이다.

    기본적으로 objectWillChange를 구현해야한다 . - publisher

    custom publisher를 제공할수있따.

    timer로써 publisher를 사용하거나,
    옵저빙는 KVO publisher를사용할수있다.

    👍ObservableOBject를 채택하면, 새로운 source of truth를 만들어낸다.
    그리고 SwiftUI는 변화에대해 어떻게 반응할지를 알려준다.
    SwiftUI는 데이터와뷰사이의 의존성을 성립할것이다.
    SwiftUI는 이 의존성을이용하여 자동적으로 뷰와 데이터를 유지해준다.

     

    • 🤔 ( 잘 모르겠다. ) 
      If you think of ObservableObject this way, it doesn't need to be exactly your data model. You can separate your data from its storage and life cycle. You can model your data using value type and manage its life cycle and side effects with a reference type. For example, you can have a single ObservableObject that centralizes your entire data model and is shared by all your views, like in the diagram here. This gives you a single place for all your logic, making it easy to reason about all the possible states and mutations in your app. Or you can focus on part of your app by having multiple ObservableObject that offer a specific projection onto your data model are are designed to expose just the data that is needed. This works better when you have a complex data model and you want to provide a more tightly scoped invalidation to part of your app.

     

     

     

     

    UI가 progress에따라 업데이트하고싶다면, @Published를 붙여준다.

    • 👍즉, ObseravbleObject를 채택하면, publisher를구현해주거나, @Published를 붙여주면된다.

    프로퍼티를 publisher로 만들어준다. ( observable, 관측당하도록 )
    뷰와데이터의 싱크를더이상걱정하지않아도된다.

    ObservableObject를이용하면,

     

    어떤데이터가필요할까,
    데이터는조작될까,

    • 항상조작, 변한다 ( var)

    데이터는어디에서오는가,

     

     

    SwiftUI는 ObservableObject를 채택하는객체와 뷰에 의존성을 만드는방법으로 3가지 프로퍼티뤠퍼를 제공한다.

    • @ObservedObject
    • @StateObject
    • @EnvironmentObject

     

     

    @ObservedObject


    * ObservableObject를 채택하는 타입을 가지고있는 프로퍼티를 알리도록,

    • SwiftUI가 뷰와 프로퍼티의의존성을 추적하도록한다.
    • 인스턴스의 주인이되지않는다.

    SwiftUI는 항상 최신상태로 만들어준다.

    간단하게, SwiftUI가 어떻게작동하는지 설명하면,

    @ObservedObject를사용하면, SwiftUI는 자동적으로 objectWillChange를 subscribe한다.

    그러므로 값이변하면 전달되어 변경된다!

    👍그럼왜, didchange가아니고, willchange일까? 


    • SwiftUI는 무엇이변경될떄를 알아냄으로써, 다른모든변화들을 하나의 업데이트로 합칠수있기때문이다.

     

     

     

    Binding은 State에서뿐만아니라, ObservableObject에서도 가능하다.

    • dollar sign을 이용하면된다.

    • 👍토글버튼을누르면 ObsesrvedObject데이터도 변경되기위해 Binding으로, $로접근했다.
      • 🔥 $로안하면 안바뀌는거야?!!!!!!!!!!! - __ - ?
        • ⭕️isOn의 타입이 애초에 Binding임.

     

     

     

    ObservableObject가 라이프사이클이 State처럼 지역적이길원할수있다.
    @ObservedObject는 자기자신이 인스턴스의주인이아니다. 그러므로 지역적으로해보자.

    @StateObject

    • 자기자신이 인스턴스의주인이되고,
    • body가작동하기전에 인스턴스화시켜준다.
    • SwiftUI는 해당뷰가 살아있는동안계속 살려둔다.

    View가만들어질떄 초기화되지않고,
    body가 돌아가기전에, 인스턴스화된다.
    더이상 View가 필요하지않다면, SwiftUI는 CoverImageLoader()를 release한다.

     

     

     

    @EnvironmentObject

    View는 값타입이기떄문에굉장히싸고,
    캡슐화하고, 재사용성을더해라!
    그럼 다이어그램이굉장히 깊어지고 넓어질수있고, 계속 ObservableObject를전달해야하는상황이발생한다.

    어쩔때는 굉장히멀리있는 곳에 전달하기위해 boilderPlate,과 무거운작업이소요될수있다

    • 왜냐하면 필요없는뷰에게도 전달해야하니까,
    • 대신우리는 !! @Environment를 통해 해결할수있따!!!

     

    view modifier이면서, 동시에 property wrapper이다.

     

     

    사용방법

    👍parentview에 ( ObservableObject를 채택하는 객체를 넣고싶은 뷰 ) , .environmentObject modifier를 사용한다

    그리고, ObservableObject를 읽고싶은 뷰에 @EnvironmentObject 프로퍼티뤠퍼를 사용하면된다!!

    그러ㅡㅁ로, 프레임워크는 필요한 어디든 전달하도록 하고, 읽는것의의존성을 추적한다.

     

    요약

     

     

     

     

     

     

     

     

    SwiftUI에서 View는……..

    View를 정의한 struct의 라이프타임과 View의라이프타임은 다르다!
    매우가볍고, 안비싸다!

    • View protocol을 채택한 struct는 매우짧은 라이프타임을갖는다.

     

     

     

    SwiftUI 업데이트 라이프사이클

    이벤트가발생하고,
    이이벤트가 Source of Truth를 변경시키고,
    👍 Source of Truth가변경됨으로써, 새로운 뷰 copy를 얻는다!!!

    • ( 새로얻어지는구나…….신기방기 )
    • 👍 👍 👍 👍 그러므로, 부모뷰가 다시그려진다면, 자식뷰도 다시, 다시생성되는데, 만약 자식뷰가 작업이큰 객체를 가지고있으면, 매번 재생성하기때문에 오래걸린다.

    더간단히표현하면, 이렇게 표현되고, 계속돌아가는상태가된다.
    굉장히빠르고, 굉장히스무스하다.

    만약, 무거운작업이 이사이클안에존재한다면? ?
    앱은 고통받는다!!!

    이를 slow update라한다.

    피하는방법으로는,

    View 초기화는 굉장히 싸다라고 보장하는것이다.

    • 그러므로, body를 pure function으로 만들어야한다.
    • 👍 부수효과가없는! pure function으로만들어야한다. ( 즉, 주어진매개변수, 외부변수를 수정하지않도록해야한다? )
    • body가얼마나호출될지를 생각하지말라, SwiftUI는 스마트하기때문에, 너의가정속에서 body가호출되지않을수있다.

     

     

     

    👍Body가 싼비용이여야하는 이유

    • 아래와같이, 만들면, ReadingListStore가 매번 힙할당이이루어지므로, 느려진다 - ㅁ -
    • 👍👍👍👍👍👍👍👍뷰struct는 정의된라이프타임을갖지않는다. 그러므로 아래와같이작성하면 객체의새로운카피를 매번생성한다. 즉 매번힙할당으로 이어진다.
      • 부모뷰로부터 전달받은 데이터가아니기때문에 매번 초기화된다. class이므로, 힙할당이매번일어난다.

    다른곳에서모델을만들고, 주입하는방법도있지만,
    🔥한줄로 표현하면좋지않을까?
    그것이바로, @Stateobject이다.
    이는 SwiftUI가 적절한시간에 ObservableObjects를 초기화해준다.
    그러므로, 더이상 불필요한힙할당을 만들지않는다…… 참조하기때문!!!!!!

    다시 돌아가보면,
    event에는 사용자이벤트터치뿐만아니라, 타이머, Notification, publisher의변화 등등이있다.
    어떤이벤트가발생하든지 사이클은 같다.

    그러한이벤트들을 Event sources라고부른다

    기존의 publisher, time, 유저이벤트뿐만아니라
    2020, 새롭게, enviriontment, binding, url, useractivities 의 변화에도 반응할수있도록했따!

    • 각각의 modifer들은 파라미터를받고, ( publisher, value , 클로저)
    • SwifTUI는 이를 적절한시간에 실행시킨다.
    • 하지만, 클로저를 mainThread에서실행하기때문에, 무거운작업은 백그라운드큐를고려하라.

     

     

     

     

     

     

     

     

     

    항상세번째질문을대답하기가어렵다 .

    그러한기준이될만한 것이있다.

    누가 데이터를 가지고있는지?

    • 데이터를사용하는 뷰가 가지고있는지? 또는 부모뷰가가지고있ㄴ느지? 또는 다른앱의시스템에서가지고있을수도있다.

    그러므로, 공통부모에서로부터 공유하든지,
    @StateObject를 사용하든지,
    전역변수롯사용하든지, 글로벌변수

     

     

     

     

     

     

     

     

     

     

     

    다음으로는 데이터의 라이프타임을 알아본다

    • Apps, Scenes 를더자세히알아보고싶다면 WWDC참고

     

     

     

    View 라이프타임
    State, StateObject 프로퍼티뤠퍼를 통해, 데이터의 라이프타임과 View의라이프타임을 결합할수있다.
    ( 🤔 ObservableObject의변화로인해, View가 다시새로만들어진다고했으니??? )

    SwiftUI의 scene

    • window마다 각각 contentView가생성되니,
    • 중요한데이터는 root에둘수있다.
      • 즉, source of truth를 windowgorup안에넣어둘수잇겠다
      • 그러므로 각각 window들이 공통된데이터로 사용할수이꼐따

    Apps 라이프타임

    • Apps은 SwiftUI로만 전체앱을작성할수있도록 해주는 아주강력한 놈이다.
    • App또한, State 등 다른 source of turh를 사용할수잇다.

    data( View? ) 는 source of truth의 라이트파임에 묶여있다.
    source of truth는 프로세스 라이프타임에 묶여있는 제한이있다.
    즉, 앱이죽거나다시시작하면, 다날라간다.

     

     

     

     

     

    👍2020, 새로운! 앱이날라가도 살아ㅣㅆ도록 하는 Storage가있다.

    • model이기보다는 model에 주입할수있는 가벼운 저장소다.

    SceneStorage
    각 scene마다 scoped된 프로퍼티뤠퍼다.

    • SwiftUI에의해완전히관리되어지는 데이터를 읽고 쓸수잇도록해주는
    • View를통해서만 접근되어진다.
    • 그러므로, 앱의상태와 관련된, 가벼운정보를 저장하기에좋다.

    여기에서는, 어떤책을 선택했는지를 저장하면좋겠따.
    ( 언제키든, 어떤책으로봤었는지볼수있도록? )

    유니크한키로 저장하고, 접근한다.
    👍그리고 자동적으로 우리대신 저장해준다!!

     

     

    AppStorage

    • d어디서든 접근할수있다.
    • UserDefault로 저장된다.
    • 그러므로, 앱의 세팅과 같은작은정보를저장하기에좋다.

     

    이렇게 키로정의하면되고,
    default로 standard user default를 사용하고,
    원한다면 커스텀이가능하다 ( 자세한건 문서를참조 )
    source of truth이므로, 바인딩이가능하다.

    'WWDC' 카테고리의 다른 글

    WWDC 19 - Introducing Combine  (0) 2021.06.19
    WWDC 20 - App essentials in SwiftUI  (0) 2021.06.19
    WWDC 20 - Introduction to SwiftUI - (2)  (0) 2021.06.17
    WWDC 20 - Introduction to SwiftUI - (1)  (0) 2021.06.17
    WWDC 21 - What’s new in UIKIt  (0) 2021.06.16

    댓글