FetchRequest 프로퍼티 래퍼에 대한 공부

이지수·2023년 7월 20일
0

Swift학습

목록 보기
8/14
post-thumbnail

FetchRequest 프로퍼티 래퍼

😫 이번 삽질을 통해.. 대충 넘겨짚고 넘어가는 건 언젠가 큰 화를 불러온다는 것을 알았다..
다음에는 삽질을 덜 하기 위해 개념을 좀 정리해본다.


@FetchRequest의 정체

FetchRequest는 프로퍼티 래퍼로 내부는 @ObservedObject로 되어 있다.


@FetchRequest 관련 이슈 상황

지금 개발하고 있는 투두앱은 Core Data를 활용한다. FetchRequest는 SwiftUI에서 Core Data Persistence의 데이터를 받아오기 위해 사용한다.
투두앱에서 임의의 날짜를 선택하면 그 날짜의 투두 리스트 데이터를 받아와 화면을 새로 그려야 한다.
나는 @FetchRequest가 @ObservedObject로 구현되어 있는지 모르고 새로운 데이터를 받기 위해 FetchRequest를 한번 더 쓰려고 한 것이다.....
부족한 공부가 부른 삽질이었다.
이미 Swift는 @FetchRequest 프로퍼티의 스토리지를 감시하고 있으므로 새로운 검색 결과를 명시적으로 받아올 필요는 없었다.


@ObservedObject

@ObservedObject란 무엇인가?
@ObservedObject 프로퍼티 래퍼를 사용하기 위해서는, ObservableObject 프로토콜을 따르는 객체가 필요하다.
@ObservedObject 프로퍼티 래퍼를 사용한 프로퍼티가 변경되면, 그 프로퍼티가 포함된 뷰는 자동으로 업데이트(새로그리기)가 된다.

아......어쩐지 @FetchRequest 프로퍼티 래퍼가 쓰인데다가 또 @State를 쓰려고 했는데 안되더라니 참 바보같은 시도였다 ㅠㅠ

@FetchRequest(
    sortDescriptors: [
        SortDescriptor(\.name, order: .reverse)
    ]
) var users: FetchedResults<User> 
//길게 선언되어 있는 프로퍼티를 보고 
//@State var users...라고 수정해보려고 했었다 

프로퍼티 래퍼는 무엇인가

생각해보니 프로퍼티 래퍼가 무엇인지에 대한 개념이 바로 안 잡혀 있는 것 같다.
프로퍼티 래퍼란 @ 기호가 따라 붙는 코드 재사용을 위한 키워드이다.
FetchRequest 정의 부분을 보면 다음과 같다.

@available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, *)
@propertyWrapper @MainActor public struct FetchRequest<Result> where Result : NSFetchRequestResult {

    /// The fetched results of the fetch request.
    ///
    /// SwiftUI returns the value associated with this property
    /// when you use ``FetchRequest`` as a property wrapper, and then access
    /// the wrapped property by name. For example, consider the following
    /// `quakes` property declaration that fetches a `Quake` type that the
    /// <doc://com.apple.documentation/documentation/CoreData/loading_and_displaying_a_large_data_feed>
    /// sample code project defines:
    ///
    ///     @FetchRequest(fetchRequest: request)
    ///     private var quakes: FetchedResults<Quake>
    ///
    /// You access the request's `wrappedValue`, which contains a
    /// ``FetchedResults`` instance, by referring to the `quakes` property
    /// by name:
    ///
    ///     Text("Found \(quakes.count) earthquakes")

💬 @FetchRequest 정의에 대해 궁금하신 분은 Xcode에서 SwiftUI를 import하고 @FetchRequest 프로퍼티를 작성한 다음에 커맨드 선택하여 정의 부분을 참고하시길 바란다.


✅ 프로퍼티래퍼의 속 알맹이는 struct였다.
이 struct 안에 프로퍼티니 메소드니 구현되어 있는 것이다.
그래서 프로퍼티 래퍼로 감싼 프로퍼티는 그 프로퍼티 래퍼에 정의된 프로퍼티나 메소드를 쓸 수 있는 것이다.
실제로 FetchRequest 프로퍼티 래퍼를 쓸 때 사용되는 init 메소드는 다음과 같이 정의되어 있었다. 익숙한 모양새다.

 @MainActor public init(entity: NSEntityDescription, 
sortDescriptors: [NSSortDescriptor], 
predicate: NSPredicate? = nil, animation: Animation? = nil)

커스텀 프로퍼티 래퍼

커스텀으로 프로퍼티 래퍼 또한 정의할 수 있다고 한다. struct 정의 부분에 @PropertyWrapper라고 정의하고, wrappedValue라는 이름의 계산 프로퍼티를 정의해 이것의 getter와 setter를 정의한다.
그래서 언뜻 보면 class와 기능이 비슷한 것 같기도?


@ObservedObject와 @State 비교

FetchRequest는 ObservedObject로 구현되었다 했다. 그럼 또 여기서 궁금한 것, ObservedObject와 State는 어떻게 다른 것인가?

@State

  • Swift는 State로 선언된 모든 프로퍼티의 스토리지를 관리(감시)하며 그 프로퍼티의 값이 변경이 되면 View의 body부분을 다시 계산하여 출력한다.
  • State 프로퍼티를 하위 뷰에서 사용하려면 Binding이 필요하다.
  • 하위 뷰로 State 프로퍼티를 넘겨 줄 땐, $ 사인을 사용한다.
  • @State는 상태나 간단한 값을 저장하는 용도로 쓰는 걸 권장되며 private로 선언해야 한다.

@ObservedObject

  • ObservableObject 객체를 구독하며, 해당 값이 바뀌면 화면을 다시 그린다(invalidate).
  • @State의 경우 특정 뷰에서만 한정해서 사용하는 String, bool 같은 간단한 프로퍼티에 사용한다면, @ObservedObject는 여러 뷰에서 공유할 수 있는 프로퍼티, 메소드, 커스텀타입에 사용한다.
  • ObservedObject가 구독하는 대상의 프로퍼티는 @Published로 선언되어 있다.
  • 또한 ObservableObject 프로토콜은 class만이 채택할 수 있다.
  • 따라서 @ObservedObject는 @ObservableObject 클래스 객체를 구독하며 @Published를 써서 사용한다.

참고

profile
iOS 개발자 꿈나무

2개의 댓글

comment-user-thumbnail
2023년 7월 20일

정말 좋은 글 감사합니다!

1개의 답글