ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Managing model data in your app
    SwiftUI/SwiftUI 애플 문서 한글화 2023. 6. 5. 13:23

    Article

     
    Hype boy
    아티스트
    NewJeans
    앨범
    NewJeans 1st EP 'New Jeans'
    발매일
    2022.08.01

    이 문서는 https://developer.apple.com/documentation/swiftui/managing-model-data-in-your-app 를 한글화한 문서입니다. 

    앱에서 모델 데이터 관리 

    앱의 데이터 모델 과 뷰 사이의 연결 

    Overview

    일반적으로 앱에서  데이터는 사용자 인터페이스나 다른 로직과 별개로 데이터 모델을 사용하여 저장하거나 처리합니다. 이렇게 모델을 분리하는 경우 모듈화를 향상시키고, 테스트 가능성을 개선하며, 앱의 작동 방식에 대해 쉽게 이해할 수 있도록 합니다. 

    기존에는 뷰 컨트롤러를 사용하여 모델과 사용자 인터페이스 간에 데이터 이동을 처리했지만 SwiftUI는 프레임워크 자체에서  뷰와 모델 사이의 동기화를 처리합니다. 데이터가 변경될 때 뷰를 업데이트하려면 데이터 모델 클래스를 ObservableObject 객체로 만들고 특별한 속성을 사용하여(@Published) 인스턴스를 선언하고 속성을 게시합니다. 사용자가 정의한 데이터 변경 사항이 모델로 다시 전달되도록 하려면 사용자 인터페이스 컨트롤을 모델 속성에 바인딩합니다. 이러한 기능을 함께 사용하면 동일한 데이터를 뷰와 모델이 공유하게 됩니다.

    관찰가능(Observable) 하게 모델 데이터 만들기 

    모델의 데이터 변경 내용을 SwiftUI에 전달하려면, 모델 클래스가 ObservableObject 프로토콜을 따라야합니다. 예를 들어, 관찰가능한 객체인 Book클래스를 아래와 같이 만들수 있습니다. 

    class Book: ObservableObject {
    }

    시스템은 클래스에 대한 ObjectWillChangePublisher 관련 타입을 자동으로 유추하고 게시된 프로퍼티(@Published 로 선언된) 의 변경된 값을 내보내는 필수 objectWillChange 메서드를 만들어 냅니다. 프로퍼티 값을 게시하려면 Published 속성(@Published)을 추가합니다.  

    class Book: ObservableObject {
        @Published var title = "Great Expectations"
    }

    프로퍼티가 게시될 필요가 없는 경우 오버헤드를 줄이기 위해 게시 속성은 사용하지 않도록 합니다. 오직 변경가능한 속성과 사용자 인터페이스와 관련이 있는 프로퍼티만 게시 속성을 사용하도록 합니다. 예를들어, Book 클래스에는 초기화 후 변경되지 않는 identifier 프로퍼티가 있을 수 있는데 이 프로퍼티는 게시 속성을 사용하지 않습니다.

    class Book: ObservableObject {
        @Published var title = "Great Expectations"
    
        let identifier = UUID() // A unique identifier that never changes.
    }

    사용자 인터페이스에 identifier를 계속 표시할 수 있지만 게시되지 않았기 때문에 SwiftUI는 값의 변경을 모니터링 할 필요가 없다는 것을 알수 있습니다. 

    관찰 가능한 객체 에 대한 모니터링

    SwiftUI가 관찰 가능한 객체(ObservableObject 프로토콜을 따르는)를 모니터링하도록 알리기 위해 ObservedObject 속성을 선언에 추가합니다.

    struct BookView: View {
        @ObservedObject var book: Book
        
        var body: some View {
            Text(book.title)
        }
    }

    관찰되고 있는 객체(ObservedObject 프로토콜을 따르는) 의 개별 프로퍼티 값을 하위 뷰에 전달할 수 있습니다. 디스크에서 새 데이터를 로드할 때 처럼 데이터가 변경되면 SwiftUI는 변경된 데이터에 영향을 받는 모든 뷰를 업데이트 합니다. 관찰 가능한 객체 전체를 하위 뷰로 전달해 뷰 계층의 수준에 걸쳐 모델 객체를 공유할 수 도 있습니다.

    struct BookView: View {
        @ObservedObject var book: Book
        
        var body: some View {
            BookEditView(book: book)
        }
    }
    
    
    struct BookEditView: View {
        @ObservedObject var book: Book
        // ...
    }

    뷰 안에서 모델 객체의 초기화 

    SwiftUI는 언제든지 뷰를 생성하거나 재 생성할 수 있므르로, 주어진 입력으로 뷰를 초기화 할 때 마다 항상 동일한 뷰가 되도록 하는 것이 중요합니다. 따라서 뷰에서 관찰되고 있는 객체를 만드는 것은 안전하지 않습니다. 대신 SwiftUI는 StateObject 속성(@StateObject)을 통해 일관되게 관찰된 객체를 유지하도록 하는 기능을 제공합니다. 다음과 같은 방법을 통해 안전하게 Book 인스턴스를 만들 수 있습니다. 

    struct LibraryView: View {
        @StateObject private var book = Book()
        
        var body: some View {
            BookView(book: book)
        }
    }

    StateObject는 ObservedObject와 같이 동작하지만, SwiftUI는 뷰의 생성 횟수와 상관없이 주어진 뷰 인스턴스에 하나의 관찰되고 있는 객체의 인스턴스를 만들고 관리합니다. 위의 예와 같이 상태 객체(@StateObject 속성으로 선언된)를 로컬에서  사용하거나 다른 뷰의 관찰되고 있는 객체 프로퍼티로 전달할 수 있습니다. 

    SwiftUI는 뷰 내에서 뷰가 재 생성되더라도 상태 객체를 재생성하지 않지만  뷰 인스턴스 마다 별개의 상태 객체 인스턴스를 생성합니다. 

    예를 들어 다음 코드의 각각의  LibraryView는 고유한 Book 인스턴스를 갖습니다.

    VStack {
        LibraryView()
        LibraryView()
    }

     

    최상위 App 인스턴스 또는 앱의 Scene 인스턴스 중 하나에서 상태 객체를 생성할 수 있습니다. 예를 들어, Book Reader 앱의 책 모음을 보관하기 위해 Library라는 관찰 가능한 객체를 정의하는 경우 앱의 최상위 수준에서 단일 라이브러리 인스턴스를 만들 수 있습니다. 

    @main
    struct BookReader: App {
        @StateObject private var library = Library()
        // ...
    }

    앱 안에서 객체의 공유 

    앱 전체에서 사용하려는 데이터 모델 객체가 있지만, 해당 모델을 계층을 통해 전달하고 싶지 않은 경우, environmentObject(_:) 수정자를 사용하여 객체를 환경 객체(@EnvironmentObject)로 사용할 수 있습니다. (계층을 통해 전달한다는 의미는 하위 뷰의 생성자로 파라미터를 통해서 전달해 사용하는 경우를 말함)

    @main
    struct BookReader: App {
        @StateObject private var library = Library()
        
        var body: some Scene {
            WindowGroup {
                LibraryView()
                    .environmentObject(library)
            }
        }
    }

    .environmentObject 한정자(view modifier)를 적용한 뷰의 모든 하위 뷰는 EnvironmentObject 속성을 사용하여 데이터에 접근할 수 있드록 프로퍼티를 선언할 수 있습니다.

    struct LibraryView: View {
        @EnvironmentObject var library: Library
        // ...
    }

    환경 객체를 사용하는 경우 위와 같이 앱 계층의 최상위 뷰에 추가할 수 있습니다. 이느 쪽이든 객체를 사용하거나 객체를 사용하는 하위 항목에서 미리보기(Preview)에 환경 변수를 추가해야 합니다.

    struct LibraryView_Previews: PreviewProvider {
        static var previews: some View {
            LibraryView()
                .environmentObject(Library()) //Preview에 환경변수 설정
        }
    }

    바인딩을 이용한 양방향 연결 

    사용자가 사용자 인터페이스를 이용해 데이터를 변경하도록 허용하는 경우(텍스트 필드를 통해 모델 데이터를 변경하는 겻과 같은) 해당 프로퍼티에 대한 바인딩을 사용합니다. 이렇게 하면 변경 사항이 자동으로 데이터 모델로 업데이트됩니다. 개체의 이름 앞에 $를 붙여 관찰 객체 또는 환경 객체 속성에 바인딩할 수 있습니다. 예를 들어 사용자가 BookEditView에 TextField를 추가하여 책의 제목을 편집할 수 있도록 하는 경우 텍스트 필드에 Book의 title 프로퍼티에 바인딩할 수 있습니다. 

    struct BookEditView: View {
        @ObservedObject var book: Book
        
        var body: some View {
            TextField("Title", text: $book.title)
        }
    }

    바인딩은 사용자가 모델 데이터를 직접 변경할 수 있도록 뷰를 데이터 모델에 직접 연결합니다.

     

    개인적인 생각 - 여기서 말하는 모델은 MVVM 의 모델과 뷰 모델이 결합된 개념으로 실제 Book 객체는 모델로 관리하고 ObservableObject 프로토콜을 따르는 부분은 BookViewModel로 분리해서 관리하도록 하는 게 더 적합해 보입니다. 

    태클은 언제나 환영!!!

    See also

    모델 데이터의 생성 

    struct StateObject 

    관찰 가능한 객체를 인스턴스화하는 프로퍼티 래퍼 타입 

     

    struct ObservedObject

    관찰 가능한 객체를 구독하고 관찰 가능한 객체가 변경 될 때 마다 뷰를 무효화 하는 프러퍼티 래퍼 타입

     

     

    댓글

Designed by Tistory.