티스토리 뷰

728x90
반응형

WWCD 24 > What’s new in Xcode 16 에서 소개된 preview 관련 API 두개..! 

 


[1] @Previewable 

- @Previewable#Preview body 내에서 사용하는 매크로 (iOS 17+)
- 이 매크로는 #Preview body 내에서 @State property 를 선언할 수 있게 해줌

(예전에는 dynamic property 를 들고 있는 뷰의 동작을 테스트하기 위해 별도의 wrapper view 를 만들고 @State property 를 선언했음) 

 

AS IS

import SwiftUI

struct SomeView: View {
    
    @Binding var text: String
    
    var body: some View {
        TextField("placeholder..", text: $text)
            .padding()
    }
}

#Preview {
    struct Preview: View {
        
        @State var text = ""
        
        var body: some View {
            SomeView(text: $text)
        }
    }

    return Preview()
}

 

 

 

TO BE 

import SwiftUI

struct SomeView: View {
    
    @Binding var text: String
    
    var body: some View {
        TextField("placeholder..", text: $text)
            .padding()
    }
}

#Preview {
    @Previewable @State var text = ""
    return SomeView(text: $text)
}

 

 

이제는 매크로가 wrapper view 를 알아서 만들어줌!

 

 

 

보일러플레이트 코드를 줄이고 더 간결하게 #preview  코드를 작성할 수 있게 됨.

 

 

 

[2] PreviewModifier 

- PreviewModifier 은 preview 들끼리 데이터나 환경을 공유할 수 있게 해주는 프로토콜 (iOS 18+)

 

- 프로토콜의 요구사항은 다음과 같고 

 

 

 

 Context 가 Void 면 makeSharedContext 는 구현안해도 됨. 

 

 

 

- preview system 의 캐싱으로 context 는 공유됨. 
(즉 해당 type 의 modifier 을 여러군데서 써도 makeSharedContext 는 한번만 호출됨) 

 

Create shared context to apply to previews. The context returned here will be cached and passed into the body method for every preview that applies a modifier of this type.

 

 

  예제  

 

[ environment 공유 ]

 

1) dynamicSize 

 

struct LargeDynamicSizePreviewModifier: PreviewModifier {
    
    func body(content: Content, context: Void) -> some View {
        content.environment(\.dynamicTypeSize, .xxxLarge)
    }
}

#Preview(traits: .modifier(LargeDynamicSizePreviewModifier())) {
    @Previewable @State var text = ""
    return SomeView(text: $text)
}

 

 

extension 사용하면 더 간단

struct LargeDynamicSizePreviewModifier: PreviewModifier {
    
    func body(content: Content, context: Void) -> some View {
        content.environment(\.dynamicTypeSize, .xxxLarge)
    }
}

extension PreviewTrait<Preview.ViewTraits> {
    static var largeDynamicSize: Self = .modifier(LargeDynamicSizePreviewModifier())
}

#Preview(traits: .largeDynamicSize) {
    @Previewable @State var text = ""
    return SomeView(text: $text)
}

 

 

2) decoration

 

struct DecorationPreviewModifier: PreviewModifier {
    
    func body(content: Content, context: Void) -> some View {
        content
            .padding()
            .background(.yellow)
            .border(.gray)
    }
}

extension PreviewTrait<Preview.ViewTraits> {
    static var decoration: Self = .modifier(DecorationPreviewModifier())
}

#Preview(traits: .decoration) {
    @Previewable @State var text = ""
    return SomeView(text: $text)
}

 

 

 

[ data 공유 ]

 

https://developer.apple.com/videos/play/wwdc2024/10135

 

 

ㄴ environment 로 namer 라는 데이터를 넘겨야하는 상황인데, 로컬파일에서 해당 데이터를 불러와서 넘길 수 있게 makeSharedContext 를 구현함.

 

ㄴ 이 예제는 간단하지만, preview 들간 데이터를 공유해야하는 상황에서 PreviewModifier 는 유용할 것.

(한번 불러오면 캐싱되어 데이터를 자주 불러와야하는 오버헤드도 없고 프리뷰들 끼리 동일 데이터를 공유할 수 있으므로..?) 

 

문서에서는 SwiftData 의 ModelContainer 사용하는 경우를 예제로 설명하니 참고. 

 

이 블로그에서 PreviewModifier 로 mock data 를 inject 하는 것의 한계를 잘 설명하니 참고.

 

Since we can’t make a new instance of our content, we can’t inject our mock data source directly into the view through its initializer. The only way we can get the data source to the view is by adding it to the environment.

 

 

 

 

 

반응형
댓글