Search code examples
swiftuipreview

SwiftUI PreviewProvider generic function for previews?


In each view I have a similar code block. Would it be possible to pass the view as a parameter? I would like to be able to pass HomeViewDemo(), MainView() or AccountView() as a parameter for the function to generate the simulations of that view for each device.

struct HomeViewDemo_Previews: PreviewProvider {
    static var previews: some View {
        HomeViewDemo()
            .previewDevice(PreviewDevice(rawValue: WPDevice.Phone.name.iPnoneSize0))
            .previewDisplayName(WPDevice.Phone.description.iPnoneSize0)
        
        HomeViewDemo()
            .previewDevice(PreviewDevice(rawValue: WPDevice.Phone.name.iPnoneSize1))
            .previewDisplayName(WPDevice.Phone.description.iPnoneSize1)
        
        HomeViewDemo()
            .previewDevice(PreviewDevice(rawValue: WPDevice.Phone.name.iPnoneSize2))
            .previewDisplayName(WPDevice.Phone.description.iPnoneSize2)
        
        HomeViewDemo()
            .previewDevice(PreviewDevice(rawValue: WPDevice.Phone.name.iPnoneSize3))
            .previewDisplayName(WPDevice.Phone.description.iPnoneSize3)
        
        HomeViewDemo()
            .previewDevice(PreviewDevice(rawValue: WPDevice.Phone.name.iPnoneSize4))
            .previewDisplayName(WPDevice.Phone.description.iPnoneSize4)
        
        HomeViewDemo()
            .previewDevice(PreviewDevice(rawValue: WPDevice.Phone.name.iPnoneSize5))
            .previewDisplayName(WPDevice.Phone.description.iPnoneSize5)
        
        HomeViewDemo()
            .previewDevice(PreviewDevice(rawValue: WPDevice.Phone.name.iPnoneSize6))
            .previewDisplayName(WPDevice.Phone.description.iPnoneSize6)
        
        HomeViewDemo()
            .previewDevice(PreviewDevice(rawValue: WPDevice.Phone.name.iPnoneSize7))
            .previewDisplayName(WPDevice.Phone.description.iPnoneSize7)
    }
}

Solution

  • I have solved this by defining a config struct:

    struct PreviewConfig: Identifiable {
    
        enum Size {
            case smallest
            case averge
            case largest
        }
    
        let size: Size
        let colorScheme: ColorScheme
        let sizeCategory: ContentSizeCategory
        let displayName: String
        let id = UUID()
    
        var device: PreviewDevice {
            switch size {
            case .smallest:
                return PreviewDevice(rawValue: "iPhone SE (1st generation)")
            case .averge:
                return PreviewDevice(rawValue: "iPhone 11 Pro")
            case .largest:
                return PreviewDevice(rawValue: "iPhone 14 Pro Max")
            }
        }
    }
    

    I also have an Array with my most commonly used ones:

    extension Array where Element == PreviewConfig {
    
        static let core = [
            PreviewConfig(
                size: .averge,
                colorScheme: .light,
                sizeCategory: .large,
                displayName: "Average iPhone"),
            PreviewConfig(
                size: .averge,
                colorScheme: .dark,
                sizeCategory: .large,
                displayName: "Average iPhone - dark mode"),
            PreviewConfig(
                size: .averge,
                colorScheme: .light,
                sizeCategory: .extraExtraLarge,
                displayName: "Average iPhone - large font"),
            PreviewConfig(
                size: .smallest,
                colorScheme: .light,
                sizeCategory: .large,
                displayName: "Smallest iPhone"),
            PreviewConfig(
                size: .largest,
                colorScheme: .light,
                sizeCategory: .large,
                displayName: "Largest iPhone")
        ]
    }
    

    and a helper method to apply them:

    extension View {
    
        func applyPreviewConfigs(_ configs: [PreviewConfig]) -> some View {
            Group {
                ForEach(configs) { config in
                    previewDevice(config.device)
                        .preferredColorScheme(config.colorScheme)
                        .environment(\.sizeCategory, config.sizeCategory)
                        .previewDisplayName(config.displayName)
                }
            }
        }
    }
    

    which allows me to simply have previews like this:

    struct HomeViewDemo_Previews: PreviewProvider {
        static var previews: some View {
            HomeViewDemo()
                .applyPreviewConfigs(.core)
        }
    }