├── .github └── workflows │ └── swift.yml ├── .swiftpm └── xcode │ ├── package.xcworkspace │ └── xcuserdata │ │ └── anton.paliakou.xcuserdatad │ │ └── UserInterfaceState.xcuserstate │ ├── xcshareddata │ └── xcschemes │ │ └── PreviewDevice.xcscheme │ └── xcuserdata │ └── anton.paliakou.xcuserdatad │ └── xcschemes │ └── xcschememanagement.plist ├── Assets └── Iphone12ColorSchemes.png ├── Documentation └── Usage.md ├── LICENSE ├── Package.swift ├── PreviewDevice.podspec ├── README.md ├── Sources └── PreviewDevice │ ├── CocoaPreivew │ ├── NSViewControllerPreview.swift │ └── NSViewPreview.swift │ ├── Device.swift │ ├── UIKitPreview │ ├── UIViewControllerPreview.swift │ └── UIViewPreview.swift │ └── View+PreviewDevice.swift └── Tests └── PreviewDeviceTests ├── DeviceAppleTVTests.swift ├── DeviceIpadTests.swift ├── DeviceIphoneTests.swift ├── DeviceIpodTests.swift ├── DeviceMacTests.swift └── DeviceWatchTests.swift /.github/workflows/swift.yml: -------------------------------------------------------------------------------- 1 | name: Swift 2 | 3 | on: 4 | push: 5 | branches: [ main ] 6 | pull_request: 7 | branches: [ main ] 8 | 9 | jobs: 10 | build: 11 | 12 | runs-on: macos-15 13 | 14 | steps: 15 | - uses: actions/checkout@v2 16 | - name: Build 17 | run: swift build -v 18 | - name: Run tests 19 | run: swift test -v 20 | 21 | -------------------------------------------------------------------------------- /.swiftpm/xcode/package.xcworkspace/xcuserdata/anton.paliakou.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Toni77777/PreviewDevice/2325173fd1daf063ac876819f99a890eb4d91a6a/.swiftpm/xcode/package.xcworkspace/xcuserdata/anton.paliakou.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /.swiftpm/xcode/xcshareddata/xcschemes/PreviewDevice.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 29 | 35 | 36 | 37 | 38 | 39 | 45 | 46 | 48 | 54 | 55 | 56 | 57 | 58 | 68 | 69 | 75 | 76 | 82 | 83 | 84 | 85 | 87 | 88 | 91 | 92 | 93 | -------------------------------------------------------------------------------- /.swiftpm/xcode/xcuserdata/anton.paliakou.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | PreviewDevice.xcscheme_^#shared#^_ 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | SuppressBuildableAutocreation 14 | 15 | PreviewDevice 16 | 17 | primary 18 | 19 | 20 | PreviewDeviceTests 21 | 22 | primary 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /Assets/Iphone12ColorSchemes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Toni77777/PreviewDevice/2325173fd1daf063ac876819f99a890eb4d91a6a/Assets/Iphone12ColorSchemes.png -------------------------------------------------------------------------------- /Documentation/Usage.md: -------------------------------------------------------------------------------- 1 | In Progress 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Anton Paliakov 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version:5.3 2 | // The swift-tools-version declares the minimum version of Swift required to build this package. 3 | 4 | import PackageDescription 5 | 6 | let package = Package( 7 | name: "PreviewDevice", 8 | platforms: [.iOS(.v13), .tvOS(.v13), .watchOS(.v6), .macOS(.v10_15)], 9 | products: [ 10 | // Products define the executables and libraries a package produces, and make them visible to other packages. 11 | .library( 12 | name: "PreviewDevice", 13 | targets: ["PreviewDevice"]), 14 | ], 15 | dependencies: [ 16 | // Dependencies declare other packages that this package depends on. 17 | // .package(url: /* package url */, from: "1.0.0"), 18 | ], 19 | targets: [ 20 | // Targets are the basic building blocks of a package. A target can define a module or a test suite. 21 | // Targets can depend on other targets in this package, and on products in packages this package depends on. 22 | .target( 23 | name: "PreviewDevice", 24 | dependencies: []), 25 | .testTarget( 26 | name: "PreviewDeviceTests", 27 | dependencies: ["PreviewDevice"]), 28 | ] 29 | ) 30 | -------------------------------------------------------------------------------- /PreviewDevice.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |spec| 2 | spec.name = "PreviewDevice" 3 | spec.version = "0.9.0" 4 | spec.summary = "PreviewDevice - library with elegant syntax for Preview Device in SwiftUI" 5 | spec.description = "PreviewDevice - is a sugar wrapper around the Apple Preview Device. SwiftUI" 6 | spec.homepage = "https://github.com/Toni77777/PreviewDevice" 7 | spec.license = "MIT" 8 | spec.author = { "Anton Paliakov" => "toxa95401@gmail.com" } 9 | spec.ios.deployment_target = "13.0" 10 | spec.osx.deployment_target = "10.15" 11 | spec.watchos.deployment_target = "6.0" 12 | spec.tvos.deployment_target = "13.0" 13 | spec.source = { :git => "https://github.com/Toni77777/PreviewDevice.git", :tag => "#{spec.version}" } 14 | spec.source_files = "Sources/PreviewDevice/*.swift" 15 | spec.swift_version = "4.2" 16 | end -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PreviewDevice 2 | ![Platforms](https://img.shields.io/cocoapods/p/PreviewDevice) 3 | [![Version](https://img.shields.io/cocoapods/v/PreviewDevice.svg?style=flat)](https://cocoapods.org/pods/PreviewDevice) 4 | ![SPM](https://img.shields.io/badge/Swift_Package_Manager-compatible-success?style=flat) 5 | ![Xcode](https://img.shields.io/badge/Xcode-13-blueviolet) 6 | [![Twitter](https://img.shields.io/badge/twitter-%20%40Toni777772-blue)](https://twitter.com/Toni777772) 7 | 8 | ## Requirements 9 | 10 | * Dev environment: Xcode 13+, macOS 12+ 11 | * iOS 13.0+, macOS 10.15+, Mac Catalyst 13.0+, tvOS 13.0+, watchOS 6.0+ 12 | 13 | ## Usage 14 | 15 | Example: 16 | 17 | ```swift 18 | import PreviewDevice 19 | 20 | struct ContentView_Previews: PreviewProvider { 21 | 22 | static var previews: some View { 23 | ContentView() 24 | .previewDevice(device: .iphone13, colorSchemes: ColorScheme.allCases) 25 | } 26 | } 27 | ``` 28 | 29 | Result 30 | 31 |
32 | 33 | 34 | ### Preview on device 35 | 36 | ```swift 37 | .previewDevice(device: .iphone12) 38 | ``` 39 | 40 | 41 | ### Preview on devices 42 | 43 | ```swift 44 | .previewDevices(device: [.iphone8, .iphone11Pro .iphone12, .iphone12ProMax]) 45 | ``` 46 | 47 | ### Preview on device with color scheme (light, dark) 48 | 49 | ```swift 50 | .previewDevice(device: .iphone12, colorScheme: .light) 51 | ``` 52 | 53 | ### Preview on device with ColorSchemes 54 | 55 | ```swift 56 | .previewDevice(device: .iphone12, colorScheme: [.light, .dark]) 57 | ``` 58 | 59 | ### Preview on device with orientation (InterfaceOrientation) 60 | 61 | ```swift 62 | .previewDevice(device: .iphone12, orientation: .portrait) 63 | ``` 64 | 65 | ### Preview on device with orientations 66 | 67 | ```swift 68 | .previewDevice(device: .iphone12, orientations: [.portrait, .landscapeLeft, .landscapeRight]) 69 | ``` 70 | 71 | ### Preview on device with orientation and color schemes 72 | 73 | ```swift 74 | .previewDevice(device: .iphone12, orientation: .portrait, colorSchemes: [.light, .dark]) 75 | ``` 76 | 77 | ## Installation 78 | 79 | ### [CocoaPods](https://guides.cocoapods.org/using/using-cocoapods.html) 80 | Specify next line in Podfile: 81 | 82 | ```ruby 83 | pod PreviewDevice 84 | ``` 85 | 86 | ### [Swift Package Manager](https://github.com/apple/swift-package-manager) 87 | 88 | Open Xcode, File -> Swift Packages -> Add Packages.. and paste library git url: 89 | 90 | ``` 91 | https://github.com/Toni77777/PreviewDevice.git 92 | ``` 93 | 94 | ## Articles 95 | [Meet PreviewDevice 0.7.0](https://dev.to/toni777772/meet-previewdevice-0-7-0-1dpg) 96 | 97 | [What's new in PreviewDevice 0.8.0](https://dev.to/toni777772/what-s-new-in-previewdevice-0-8-0-5dc0) 98 | 99 | ## License 100 | PreviewDevice is released under the MIT license. 101 | -------------------------------------------------------------------------------- /Sources/PreviewDevice/CocoaPreivew/NSViewControllerPreview.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NSViewControllerPreview.swift 3 | // 4 | // 5 | // Created by Anton Paliakou on 10/15/21. 6 | // 7 | 8 | #if canImport(AppKit) && canImport(SwiftUI) 9 | import AppKit 10 | import SwiftUI 11 | 12 | public struct NSViewControllerPreview: NSViewControllerRepresentable { 13 | 14 | // MARK: - Properties 15 | 16 | private let viewController: NSViewController 17 | 18 | // MARK: - Init 19 | 20 | public init(viewController: NSViewController) { 21 | self.viewController = viewController 22 | } 23 | 24 | // MARK: - NSViewControllerRepresentable 25 | 26 | public func makeNSViewController(context: Context) -> NSViewController { 27 | viewController 28 | } 29 | 30 | public func updateNSViewController(_ nsViewController: NSViewController, context: Context) { 31 | } 32 | } 33 | #endif 34 | -------------------------------------------------------------------------------- /Sources/PreviewDevice/CocoaPreivew/NSViewPreview.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NSViewPreview.swift 3 | // 4 | // 5 | // Created by Anton Paliakou on 10/16/21. 6 | // 7 | 8 | #if canImport(AppKit) && canImport(SwiftUI) 9 | import AppKit 10 | import SwiftUI 11 | 12 | public struct NSViewPreview: NSViewRepresentable { 13 | 14 | // MARK: - Properties 15 | 16 | private let view: NSView 17 | 18 | // MARK: - Init 19 | 20 | public init(view: NSView) { 21 | self.view = view 22 | } 23 | 24 | // MARK: - NSViewRepresentable 25 | 26 | public func makeNSView(context: Context) -> NSView { 27 | view 28 | } 29 | 30 | public func updateNSView(_ nsView: NSView, context: Context) { 31 | } 32 | } 33 | #endif 34 | -------------------------------------------------------------------------------- /Sources/PreviewDevice/Device.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Device.swift 3 | // PreviewDevice 4 | // 5 | // 6 | // Created by Anton Paliakou on 8/26/21. 7 | // 8 | 9 | import Foundation 10 | 11 | public enum Device: String { 12 | 13 | // MARK: - iPhone 14 | 15 | case iphone6Plus = "iPhone 6 Plus" 16 | case iphone6 = "iPhone 6" 17 | case iphone6s = "iPhone 6s" 18 | case iphone6sPlus = "iPhone 6s Plus" 19 | case iphoneSE_1Gen = "iPhone SE (1st generation)" 20 | case iphone7 = "iPhone 7" 21 | case iphone7Plus = "iPhone 7 Plus" 22 | case iphone8 = "iPhone 8" 23 | case iphone8Plus = "iPhone 8 Plus" 24 | case iphoneX = "iPhone X" 25 | case iphoneXs = "iPhone Xs" 26 | case iphoneXsMax = "iPhone Xs Max" 27 | case iphoneXr = "iPhone Xʀ" 28 | case iphone11 = "iPhone 11" 29 | case iphone11Pro = "iPhone 11 Pro" 30 | case iphone11ProMax = "iPhone 11 Pro Max" 31 | case iphoneSE_2Gen = "iPhone SE (2nd generation)" 32 | case iphone12Mini = "iPhone 12 mini" 33 | case iphone12 = "iPhone 12" 34 | case iphone12Pro = "iPhone 12 Pro" 35 | case iphone12ProMax = "iPhone 12 Pro Max" 36 | case iphone13Pro = "iPhone 13 Pro" 37 | case iphone13ProMax = "iPhone 13 Pro Max" 38 | case iphone13Mini = "iPhone 13 mini" 39 | case iphone13 = "iPhone 13" 40 | case iphoneSE_3Gen = "iPhone SE (3rd generation)" 41 | case iphone14 = "iPhone 14" 42 | case iphone14Plus = "iPhone 14 Plus" 43 | case iphone14Pro = "iPhone 14 Pro" 44 | case iphone14ProMax = "iPhone 14 Pro Max" 45 | case iphone15 = "iPhone 15" 46 | case iphone15Plus = "iPhone 15 Plus" 47 | case iphone15Pro = "iPhone 15 Pro" 48 | case iphone15ProMax = "iPhone 15 Pro Max" 49 | case iphone16 = "iPhone 16" 50 | case iphone16Plus = "iPhone 16 Plus" 51 | case iphone16Pro = "iPhone 16 Pro" 52 | case iphone16ProMax = "iPhone 16 Pro Max" 53 | 54 | // MARK: - iPad 55 | 56 | case ipad2 = "iPad 2" 57 | case ipadRetina = "iPad Retina" 58 | case ipadAir = "iPad Air" 59 | case ipadMini2 = "iPad mini 2" 60 | case ipadMini3 = "iPad mini 3" 61 | case ipadMini4 = "iPad mini 4" 62 | case ipadMini5 = "iPad mini (5th generation)" 63 | case ipadMini6 = "iPad mini (6th generation)" 64 | case ipadAir2 = "iPad Air 2" 65 | case ipadPro9_7inch = "iPad Pro (9.7-inch)" 66 | case ipadPro12_9inch = "iPad Pro (12.9-inch)" 67 | case ipad_5Gen = "iPad (5th generation)" 68 | case ipadPro12_9inch_2Gen = "iPad Pro (12.9-inch) (2nd generation)" 69 | case ipadPro10_5inch = "iPad Pro (10.5-inch)" 70 | case ipad_6Gen = "iPad (6th generation)" 71 | case ipad_7Gen = "iPad (7th generation)" 72 | case ipad_8Gen = "iPad (8th generation)" 73 | case ipad_9Gen = "iPad (9th generation)" 74 | case ipadPro11inch_1Gen = "iPad Pro (11-inch) (1st generation)" 75 | case ipadPro12_9inch_3Gen = "iPad Pro (12.9-inch) (3rd generation)" 76 | case ipadPro11inch_2Gen = "iPad Pro (11-inch) (2nd generation)" 77 | case ipadPro12_9inch_4Gen = "iPad Pro (12.9-inch) (4th generation)" 78 | case ipadAir_3Gen = "iPad Air (3rd generation)" 79 | case ipadAir_4Gen = "iPad Air (4th generation)" 80 | case ipadPro11inch_3Gen = "iPad Pro (11-inch) (3rd generation)" 81 | case ipadPro12_9inch_5Gen = "iPad Pro (12.9-inch) (5th generation)" 82 | case ipadAir_5Gen = "iPad Air (5th generation)" 83 | 84 | 85 | // MARK: - iPod 86 | 87 | case ipod7Gen = "iPod touch (7th generation)" 88 | 89 | // MARK: - Mac 90 | 91 | case mac = "Mac" 92 | case macCatalyst = "Mac Catalyst" 93 | 94 | // MARK: - Apple TV 95 | 96 | case appleTV = "Apple TV" 97 | case appleTV4K = "Apple TV 4K" 98 | case appleTV4K1080p = "Apple TV 4K (at 1080p)" 99 | case appleTV4K_2Gen = "Apple TV 4K (2nd generation)" 100 | case appleTV4K1080p_2Gen = "Apple TV 4K (at 1080p) (2nd generation)" 101 | 102 | // MARK: - Apple Watch 103 | 104 | case watch_38mm = "Apple Watch (38mm)" 105 | case watch_42mm = "Apple Watch (42mm)" 106 | case watchSeries2_38mm = "Apple Watch Series 2 (38mm)" 107 | case watchSeries2_42mm = "Apple Watch Series 2 (42mm)" 108 | case watchSeries3_38mm = "Apple Watch Series 3 (38mm)" 109 | case watchSeries3_42mm = "Apple Watch Series 3 (42mm)" 110 | case watchSeries4_40mm = "Apple Watch Series 4 (40mm)" 111 | case watchSeries4_44mm = "Apple Watch Series 4 (44mm)" 112 | case watchSeries5_40mm = "Apple Watch Series 5 (40mm)" 113 | case watchSeries5_44mm = "Apple Watch Series 5 (44mm)" 114 | case watchSE_40mm = "Apple Watch SE (40mm)" 115 | case watchSE_44mm = "Apple Watch SE (44mm)" 116 | case watchSeries6_40mm = "Apple Watch Series 6 (40mm)" 117 | case watchSeries6_44mm = "Apple Watch Series 6 (44mm)" 118 | case watchSeries7_41mm = "Apple Watch Series 7 (41mm)" 119 | case watchSeries7_45mm = "Apple Watch Series 7 (45mm)" 120 | case watchSE_2Gen_40mm = "Apple Watch SE (40mm) (2nd generation)" 121 | case watchSE_2Gen_44mm = "Apple Watch SE (44mm) (2nd generation)" 122 | case watchSeries8_41mm = "Apple Watch Series 8 (41mm)" 123 | case watchSeries8_45mm = "Apple Watch Series 8 (45mm)" 124 | case watchUltra_49mm = "Apple Watch Ultra (49mm)" 125 | } 126 | -------------------------------------------------------------------------------- /Sources/PreviewDevice/UIKitPreview/UIViewControllerPreview.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UIViewControllerPreview.swift 3 | // 4 | // 5 | // Created by Anton Paliakou on 10/15/21. 6 | // 7 | 8 | #if canImport(UIKit) && canImport(SwiftUI) 9 | import SwiftUI 10 | import UIKit 11 | 12 | public struct UIViewControllerPreview: UIViewControllerRepresentable { 13 | 14 | // MARK: - Properties 15 | 16 | private let viewController: UIViewController 17 | 18 | // MARK: - Init 19 | 20 | public init(viewController: UIViewController) { 21 | self.viewController = viewController 22 | } 23 | 24 | // MARK: - UIViewControllerRepresentable 25 | 26 | public func makeUIViewController(context: Context) -> UIViewController { 27 | viewController 28 | } 29 | 30 | public func updateUIViewController(_ uiViewController: UIViewController, context: Context) { 31 | } 32 | } 33 | 34 | #endif 35 | -------------------------------------------------------------------------------- /Sources/PreviewDevice/UIKitPreview/UIViewPreview.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UIViewPreview.swift 3 | // 4 | // 5 | // Created by Anton Paliakou on 10/15/21. 6 | // 7 | 8 | #if canImport(UIKit) && canImport(SwiftUI) 9 | import SwiftUI 10 | import UIKit 11 | 12 | public struct UIViewPreview: UIViewRepresentable { 13 | 14 | // MARK: - Properties 15 | 16 | private let view: UIView 17 | 18 | // MARK: - Init 19 | 20 | public init(view: UIView) { 21 | self.view = view 22 | } 23 | 24 | // MARK: - UIViewRepresentable 25 | 26 | public func makeUIView(context: Context) -> UIViewType { 27 | self.view 28 | } 29 | 30 | public func updateUIView(_ uiView: UIView, context: Context) { 31 | } 32 | } 33 | 34 | #endif 35 | -------------------------------------------------------------------------------- /Sources/PreviewDevice/View+PreviewDevice.swift: -------------------------------------------------------------------------------- 1 | // 2 | // View+PreviewDevice.swift 3 | // PreviewDevice 4 | // 5 | // Created by Anton Paliakou on 8/26/21. 6 | // 7 | 8 | #if canImport(SwiftUI) 9 | import SwiftUI 10 | 11 | @available(iOS 13.0, OSX 10.15, macCatalyst 13.0, tvOS 13.0, watchOS 6.0, *) 12 | public extension View { 13 | 14 | func previewDevice(device: Device) -> some View { 15 | previewDevice(PreviewDevice(rawValue: device.rawValue)) 16 | .previewDisplayName(device.rawValue) 17 | } 18 | 19 | func previewDevices(devices: [Device]) -> some View { 20 | ForEach(devices, id: \.self) { device in 21 | previewDevice(device: device) 22 | } 23 | } 24 | } 25 | 26 | @available(iOS 13.0, macOS 11.0, macCatalyst 13.0, tvOS 13.0, watchOS 6.0, *) 27 | public extension View { 28 | 29 | func previewDevice(device: Device, colorScheme: ColorScheme) -> some View { 30 | previewDevice(device: device) 31 | .preferredColorScheme(colorScheme) 32 | } 33 | 34 | func previewDevice(device: Device, colorSchemes: [ColorScheme]) -> some View { 35 | ForEach(0.. some View { 46 | previewDevice(device: device) 47 | .previewInterfaceOrientation(orientation) 48 | } 49 | 50 | func previewDevice(device: Device, orientations: [InterfaceOrientation]) -> some View { 51 | ForEach(0.. some View { 58 | ForEach(0..