├── .swift-version ├── .xcode-version ├── LICENSE ├── Package.swift ├── README.md ├── Sources ├── Device.swift ├── Extensions │ ├── Device+Comparison.swift │ ├── Device+Display.swift │ ├── Device+Family.swift │ ├── Device+MarketingName.swift │ ├── Device+ModelNumbers.swift │ └── Device+ProductLines.swift ├── Info-tvOS.plist ├── Info.plist ├── Model │ ├── Display.swift │ ├── Family.swift │ ├── Lines.swift │ └── ThingyError.swift ├── Protocols.swift ├── RawDevice.swift └── Thingy.h ├── Tests ├── ComparisonTests.swift ├── DeviceTests.swift ├── Info.plist ├── RawDeviceTests.swift └── XCTest+Throwable.swift ├── Thingy.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist └── xcshareddata │ └── xcschemes │ ├── Tests.xcscheme │ ├── Thingy iOS.xcscheme │ ├── Thingy tvOS.xcscheme │ └── Thingy watchOS.xcscheme ├── codecov.yml └── docs ├── README.md ├── enums ├── ColorSpace.md ├── Device.md ├── Display.ColorSpace.md ├── Display.Size.md ├── Family.md ├── Lines.iPad.md ├── Lines.iPhone.md ├── Lines.md ├── Size.md ├── ThingyError.md └── iPad.md ├── extensions ├── Device.md ├── Display.Size.md ├── Family.md └── RawDevice.md ├── protocols └── MarketingProtocol.md ├── structs ├── Display.md └── Lines.md └── typealiases └── ProductLine.md /.swift-version: -------------------------------------------------------------------------------- 1 | 5.2.4 2 | -------------------------------------------------------------------------------- /.xcode-version: -------------------------------------------------------------------------------- 1 | 11.6 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE 2 | Version 2, December 2004 3 | 4 | Copyright (C) 2004 Sam Hocevar 5 | 6 | Everyone is permitted to copy and distribute verbatim or modified 7 | copies of this license document, and changing it is allowed as long 8 | as the name is changed. 9 | 10 | DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE 11 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 12 | 13 | 0. You just DO WHAT THE FUCK YOU WANT TO. 14 | 15 | -------------------------------------------------------------------------------- /Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version:5.0 2 | // 3 | // Package.swift 4 | // Thingy 5 | // 6 | // Created by Bojan Dimovski on 03.4.17. 7 | // Copyright (c) 2017 Bojan Dimovski. 8 | // 9 | // DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE 10 | // Version 2, December 2004 11 | // 12 | // Copyright (C) 2004 Sam Hocevar 13 | // 14 | // Everyone is permitted to copy and distribute verbatim or modified 15 | // copies of this license document, and changing it is allowed as long 16 | // as the name is changed. 17 | // 18 | // DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE 19 | // TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 20 | // 21 | // 0. You just DO WHAT THE FUCK YOU WANT TO. 22 | // 23 | // 24 | 25 | import PackageDescription 26 | 27 | let package = Package( 28 | name: "Thingy", 29 | platforms: [ 30 | .iOS(.v8), 31 | .tvOS(.v9), 32 | .watchOS(.v2) 33 | ], 34 | products: [ 35 | .library(name: "Thingy", targets: ["Thingy"]) 36 | ], 37 | dependencies: [], 38 | targets: [ 39 | .target( 40 | name: "Thingy", 41 | path: "Sources"), 42 | .testTarget( 43 | name: "ThingyTests", 44 | dependencies: ["Thingy"], 45 | path: "Tests"), 46 | ] 47 | ) 48 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Thingy 2 | A modern device detection and querying library. 3 | 4 | [![Build Status](https://app.bitrise.io/app/5638e96850380bcf/status.svg?token=ATvHHXw6RMbyde-i0FPV_w&branch=master)](https://app.bitrise.io/app/5638e96850380bcf) 5 | [![GitHub license](https://img.shields.io/badge/license-WTFPL-green)](https://raw.githubusercontent.com/bojan/Thingy/master/LICENSE) 6 | [![SPM](https://img.shields.io/badge/SPM-compatible-brightgreen.svg)](https://github.com/apple/swift-package-manager) 7 | [![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg)](https://github.com/Carthage/Carthage) 8 | [![codecov](https://codecov.io/gh/bojan/Thingy/branch/master/graph/badge.svg)](https://codecov.io/gh/bojan/Thingy) 9 | 10 | ## Features 11 | 12 | - [x] Swift 5 support 13 | - [x] Modern syntax 14 | - [x] Documentation 15 | - [x] Device detection 16 | - [x] Device family detection 17 | - [x] Device screens 18 | - [x] Simulator detection 19 | - [x] Simple querying 20 | 21 | ## Requirements 22 | 23 | ## Installation 24 | 25 | ### Swift Packager Manager (SPM) 26 | 27 | Use Xcode 11 to add Thingy as a package or update your `Package.swift` for manual setups: 28 | 29 | ```swift 30 | import PackageDescription 31 | 32 | let package = Package( 33 | name: "HelloWorld", 34 | dependencies: [ 35 | .Package(url: "https://github.com/bojan/Thingy.git", "3.0.0") 36 | ] 37 | ) 38 | ``` 39 | 40 | ### Carthage 41 | 42 | Add this to your `Cartfile`: 43 | 44 | ```ogdl 45 | github "bojan/Thingy" 46 | ``` 47 | 48 | ### Manually 49 | 50 | Add the repository as a submodule to your project. 51 | 52 | ```bash 53 | git submodule add https://github.com/bojan/Thingy.git Vendor/Thingy 54 | ``` 55 | 56 | Open the newly created folder in Finder and drag `Thingy.xcodeproj` to your project. 57 | 58 | In your project's settings, select your target and under **General** > **Embedded Binaries**, add the framework depending on the target OS (iOS, watchOS or tvOS). 59 | 60 | ## Usage 61 | 62 | Import the module where needed: 63 | 64 | ```swift 65 | import Thingy 66 | ``` 67 | 68 | ### Device properties 69 | 70 | Inspect the current device: 71 | 72 | ```swift 73 | 74 | let myDevice = Device() 75 | 76 | // Compare models or product families 77 | if myDevice.family == .tv { 78 | print("This is an Apple TV device.") 79 | } 80 | 81 | if myDevice.model != .iPhoneXSMax { 82 | print("This is NOT an iPhone XS Max.") 83 | } 84 | 85 | // Pretty printed device properties 86 | 87 | print(myDevice.family.marketingName) // e.g. iPad 88 | print(myDevice.model.marketingName) // e.g. iPhone 7 Plus 89 | print(myDevice.productLine.marketingName) // e.g. Air 90 | 91 | ``` 92 | 93 | ### Model comparison 94 | 95 | ```swift 96 | let myDevice = Device() 97 | 98 | do { 99 | let result = try myDevice.isEqual(to: Thingy.iPadPro12Inch) 100 | } 101 | catch { 102 | print("The devices are incompatible.") 103 | } 104 | 105 | do { 106 | let result = try myDevice.isOlder(than: Thingy.iPadPro10Inch) 107 | } 108 | catch { 109 | print("The devices are incompatible.") 110 | } 111 | ``` 112 | 113 | ### Device size 114 | 115 | ```swift 116 | let myDevice = Device() 117 | 118 | if myDevice.display == .screen10_5Inch { 119 | print("This is the 10.5in iPad Pro.") 120 | } 121 | 122 | ``` 123 | 124 | ## Contributions 125 | 126 | All contributions and suggestions are welcome and very much appreciated. 127 | 128 | Should you have a feature request or a problem that you may experience, feel free to [open an issue](https://github.com/bojan/Thingy/issues/new). 129 | 130 | If you are willing to contribute by adding a feature or squashing a bug or two, please submit a pull request. 131 | 132 | ## Author 133 | 134 | Bojan Dimovski 135 | - [LinkedIn](http://linkedin.com/in/bdimovski) 136 | 137 | ## License 138 | 139 | Thingy is available under the WTFPL license. Check the [LICENSE](https://raw.githubusercontent.com/bojan/Thingy/master/LICENSE) file for more info. 140 | -------------------------------------------------------------------------------- /Sources/Device.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Thingy 3 | // Thingy 4 | // 5 | // Created by Bojan Dimovski on 21.11.16. 6 | // Copyright © 2017 Bojan Dimovski. All rights reserved. 7 | // 8 | // DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE 9 | // Version 2, December 2004 10 | // 11 | // Copyright (C) 2004 Sam Hocevar 12 | // 13 | // Everyone is permitted to copy and distribute verbatim or modified 14 | // copies of this license document, and changing it is allowed as long 15 | // as the name is changed. 16 | // 17 | // DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE 18 | // TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 19 | // 20 | // 0. You just DO WHAT THE FUCK YOU WANT TO. 21 | // 22 | // 23 | 24 | import Foundation 25 | 26 | /// A type that describes the specific device model. 27 | /// 28 | /// For iOS devices, only devices supporting iOS 8 are included. 29 | public enum Device: CaseIterable { 30 | 31 | /// - iPhone4S: iPhone 4S. 32 | case iPhone4S 33 | /// - iPhone5: All iPhone 5 variants. 34 | case iPhone5 35 | /// - iPhone5s: All iPhone 5s variants. 36 | case iPhone5s 37 | /// - iPhone5c: All iPhone 5c variants. 38 | case iPhone5c 39 | /// - iPhone6: All iPhone 6 variants. 40 | case iPhone6 41 | /// - iPhone6Plus: All iPhone 6 Plus variants. 42 | case iPhone6Plus 43 | /// - iPhone6s: All iPhone 6s variants. 44 | case iPhone6s 45 | /// - iPhone6sPlus: All iPhone 6s Plus variants. 46 | case iPhone6sPlus 47 | /// - iPhoneSE: All iPhone SE variants. 48 | case iPhoneSE 49 | /// - iPhoneSE2: All iPhone SE (2020) variants. 50 | case iPhoneSE2G 51 | /// - iPhone7: All iPhone 7 variants. 52 | case iPhone7 53 | /// - iPhone7Plus: All iPhone 7 Plus variants. 54 | case iPhone7Plus 55 | /// - iPhone8: All iPhone 8 variants. 56 | case iPhone8 57 | /// - iPhone8Plus: All iPhone 8 Plus variants. 58 | case iPhone8Plus 59 | /// - iPhoneX: All iPhone X variants. 60 | case iPhoneX 61 | /// - iPhoneXR: All iPhone XR variants. 62 | case iPhoneXR 63 | /// - iPhoneXS: All iPhone XS variants. 64 | case iPhoneXS 65 | /// - iPhoneXSMax: All iPhone XS Max variants. 66 | case iPhoneXSMax 67 | /// - iPhone11: All iPhone 11 variants. 68 | case iPhone11 69 | /// - iPhone11Pro: All iPhone 11 Pro variants. 70 | case iPhone11Pro 71 | /// - iPhone11ProMax: All iPhone 11 Pro Max variants. 72 | case iPhone11ProMax 73 | 74 | /// - iPad2: All iPad 2 variants. 75 | case iPad2 76 | /// - iPad3: All iPad 3 variants. 77 | case iPad3 78 | /// - iPad4: All iPad 4 variants. 79 | case iPad4 80 | /// - iPad5: All iPad (2017) variants. 81 | case iPad5 82 | /// - iPad6: All iPad (2018) variants. 83 | case iPad6 84 | /// - iPad7: All iPad (2019) variants. 85 | case iPad7 86 | /// - iPadMini: The original iPad mini in all variants. 87 | case iPadMini 88 | /// - iPadMini2: All iPad mini 2 variants. 89 | case iPadMini2 90 | /// - iPadMini3: All iPad mini 3 variants. 91 | case iPadMini3 92 | /// - iPadMini4: All iPad mini 4 variants. 93 | case iPadMini4 94 | /// - iPadMini5: All iPad mini 5 variants. 95 | case iPadMini5 96 | /// - iPadAir: The original iPad Air in all variants. 97 | case iPadAir 98 | /// - iPadAir2: All iPad Air 2 variants. 99 | case iPadAir2 100 | /// - iPadAir3: All iPad Air 3 variants. 101 | case iPadAir3 102 | /// - iPadPro12Inch: The 1st generation 12.9 inch iPad Pro in all variants. 103 | case iPadPro12Inch 104 | /// - iPadPro9Inch: The 9.7 inch iPad Pro in all variants. 105 | case iPadPro9Inch 106 | /// - iPadPro12Inch2G: The 2nd generation 12 inch iPad Pro in all variants. 107 | case iPadPro12Inch2G 108 | /// - iPadPro10Inch: The 10.5 inch iPad Pro in all variants. 109 | case iPadPro10Inch 110 | /// - iPadPro12Inch3G: The 3rd generation 12.9 inch iPad Pro in all variants. 111 | case iPadPro12Inch3G 112 | /// - iPadPro11Inch: The 11 inch iPad Pro in all variants. 113 | case iPadPro11Inch 114 | 115 | /// - iPodTouch5G: All iPod touch 5G variants. 116 | case iPodTouch5G 117 | /// - iPodTouch6G: All iPod touch 6G variants. 118 | case iPodTouch6G 119 | /// - iPodTouch7G: All iPod touch 7G variants. 120 | case iPodTouch7G 121 | 122 | /// - appleTV4: Apple TV 4. 123 | case appleTV4 124 | 125 | /// - appleTV4K: Apple TV 4K. 126 | case appleTV4K 127 | 128 | /// - watch: The original Apple Watch. 129 | case watch(Display.Size) 130 | /// - watchSeries1: Apple Watch Series 1. 131 | case watchSeries1(Display.Size) 132 | /// - watchSeries2: Apple Watch Series 2. 133 | case watchSeries2(Display.Size) 134 | /// - watchSeries3: Apple Watch Series 3. 135 | case watchSeries3(Display.Size) 136 | /// - watchSeries4: Apple Watch Series 4. 137 | case watchSeries4(Display.Size) 138 | /// - watchSeries5: Apple Watch Series 5. 139 | case watchSeries5(Display.Size) 140 | 141 | /// - simulator: A simulator for the associated device model. 142 | indirect case simulator(Device) 143 | 144 | /// - unknown: An unknown or a future device within the associated family. 145 | case unknown(Family) 146 | 147 | /// All real-device values. 148 | public static let allCases: [Device] = [ 149 | .iPhone4S, 150 | .iPhone5, 151 | .iPhone5c, 152 | .iPhone5s, 153 | .iPhone6, 154 | .iPhone6Plus, 155 | .iPhone6s, 156 | .iPhone6sPlus, 157 | .iPhoneSE, 158 | .iPhoneSE2G, 159 | .iPhone7, 160 | .iPhone7Plus, 161 | .iPhone8, 162 | .iPhone8Plus, 163 | .iPhoneX, 164 | .iPhoneXR, 165 | .iPhoneXS, 166 | .iPhoneXSMax, 167 | .iPhone11, 168 | .iPhone11Pro, 169 | .iPhone11ProMax, 170 | .iPodTouch5G, 171 | .iPodTouch6G, 172 | .iPodTouch7G, 173 | .appleTV4, 174 | .appleTV4K, 175 | .watch(.screen38mm), 176 | .watch(.screen42mm), 177 | .watchSeries1(.screen38mm), 178 | .watchSeries1(.screen42mm), 179 | .watchSeries2(.screen38mm), 180 | .watchSeries2(.screen42mm), 181 | .watchSeries3(.screen38mm), 182 | .watchSeries3(.screen42mm), 183 | .watchSeries4(.screen40mm), 184 | .watchSeries4(.screen44mm), 185 | .watchSeries5(.screen40mm), 186 | .watchSeries5(.screen44mm), 187 | .iPad2, 188 | .iPad3, 189 | .iPad4, 190 | .iPad5, 191 | .iPad6, 192 | .iPad7, 193 | .iPadAir, 194 | .iPadAir2, 195 | .iPadAir3, 196 | .iPadPro12Inch, 197 | .iPadPro9Inch, 198 | .iPadPro12Inch2G, 199 | .iPadPro10Inch, 200 | .iPadPro12Inch3G, 201 | .iPadPro11Inch, 202 | .iPadMini, 203 | .iPadMini2, 204 | .iPadMini3, 205 | .iPadMini4, 206 | .iPadMini5, 207 | ] 208 | 209 | } 210 | 211 | public extension Device { 212 | /// Creates a custom device out of a valid identifier. 213 | /// 214 | /// If a device cannot be created, returns an unknown device. 215 | /// 216 | /// - Parameter identifier: A device identifier, e.g. "iPhone9,2", "iPad6,11.", "AppleTV5,3". 217 | init?(identifier: String? = nil) { 218 | guard let device = RawDevice(identifier: identifier).device 219 | else { 220 | return nil 221 | } 222 | 223 | self = device 224 | } 225 | } 226 | -------------------------------------------------------------------------------- /Sources/Extensions/Device+Comparison.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Device 3 | // Thingy 4 | // 5 | // Created by Bojan Dimovski on 19/01/2020. 6 | // Copyright © 2020 Bojan Dimovski. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public extension Device { 12 | 13 | /// Checks if the current device is the same as the compared model. 14 | /// 15 | /// - Parameter to: A model to compare the current device against. 16 | /// - Returns: True if the device is the same, and false otherwise. 17 | /// - Throws: An error when comparing incompatible families, product lines or unknown products. 18 | func isEqual(to compared: Device) throws -> Bool { 19 | try Device.compare(lhs: self, rhs: compared, sign: ==) 20 | } 21 | 22 | /// Checks if the current device is newer (or same) than the compared model. 23 | /// 24 | /// - Parameter than: A model to compare the current device against. 25 | /// - Returns: True if the device is newer or the same, and false if it's older. 26 | /// - Throws: An error when comparing incompatible families, product lines or unknown products. 27 | func isNewerOrEqual(than compared: Device) throws -> Bool { 28 | try Device.compare(lhs: self, rhs: compared, sign: >=) 29 | } 30 | 31 | /// Checks if the current device is older than the compared model. 32 | /// 33 | /// - Parameter than: A model to compare the current device against. 34 | /// - Returns: True if the device is older, and false if it's newer or the same. 35 | /// - Throws: An error when comparing incompatible families, product lines or unknown products. 36 | func isOlder(than compared: Device) throws -> Bool { 37 | try Device.compare(lhs: self, rhs: compared, sign: <) 38 | } 39 | 40 | internal static func compare(lhs: Device, rhs: Device, sign: (RawDevice, RawDevice) -> Bool) throws -> Bool { 41 | if case .unknown = lhs { 42 | throw ThingyError.incomparableUnknownProduct 43 | } 44 | 45 | if case .unknown = rhs { 46 | throw ThingyError.incomparableUnknownProduct 47 | } 48 | 49 | guard lhs.family == rhs.family 50 | else { 51 | throw ThingyError.incomparableFamilies 52 | } 53 | 54 | if let lhsProductLine = lhs.productLine, 55 | let rhsProductLine = rhs.productLine, 56 | lhsProductLine != rhsProductLine { 57 | throw ThingyError.incomparableProductLines 58 | } 59 | 60 | let lhsRaw = RawDevice(family: lhs.family, modelNumber: lhs.lowestNumber) 61 | let rhsRaw = RawDevice(family: rhs.family, modelNumber: rhs.lowestNumber) 62 | 63 | return sign(lhsRaw, rhsRaw) 64 | } 65 | 66 | } 67 | -------------------------------------------------------------------------------- /Sources/Extensions/Device+Display.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Device 3 | // Thingy 4 | // 5 | // Created by Bojan Dimovski on 19/01/2020. 6 | // Copyright © 2020 Bojan Dimovski. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public extension Device { 12 | 13 | /// Returns the display size in inches. 14 | var displaySize: Display.Size { 15 | display.size 16 | } 17 | 18 | /// Returns an extended info on the display, including all resolutions, scale and density. 19 | var display: Display { 20 | switch self { 21 | case .iPhone4S: 22 | return Display(size: .screen3_5Inch, resolution: CGSize(width: 320, height: 480), physicalResolution: CGSize(width: 640, height: 960), renderedResolution: CGSize(width: 640, height: 960), scale: 2.0, density: 326, hasTrueTone: false, colorSpace: .sRGB) 23 | 24 | case .iPhone5, 25 | .iPhone5c, 26 | .iPhone5s, 27 | .iPhoneSE, 28 | .iPodTouch5G, 29 | .iPodTouch6G, 30 | .iPodTouch7G: 31 | return Display(size: .screen4Inch, resolution: CGSize(width: 320, height: 568), physicalResolution: CGSize(width: 640, height: 1136), renderedResolution: CGSize(width: 640, height: 1136), scale: 2.0, density: 326, hasTrueTone: false, colorSpace: .sRGB) 32 | 33 | case .iPhone6, 34 | .iPhone6s: 35 | return Display(size: .screen4_7Inch, resolution: CGSize(width: 375, height: 667), physicalResolution: CGSize(width: 750, height: 1334), renderedResolution: CGSize(width: 750, height: 1334), scale: 2.0, density: 326, hasTrueTone: false, colorSpace: .sRGB) 36 | 37 | case .iPhone7: 38 | return Display(size: .screen4_7Inch, resolution: CGSize(width: 375, height: 667), physicalResolution: CGSize(width: 750, height: 1334), renderedResolution: CGSize(width: 750, height: 1334), scale: 2.0, density: 326, hasTrueTone: false, colorSpace: .p3) 39 | 40 | case .iPhone8, 41 | .iPhoneSE2G: 42 | return Display(size: .screen4_7Inch, resolution: CGSize(width: 375, height: 667), physicalResolution: CGSize(width: 750, height: 1334), renderedResolution: CGSize(width: 750, height: 1334), scale: 2.0, density: 326, hasTrueTone: true, colorSpace: .p3) 43 | 44 | case .iPhone6Plus, 45 | .iPhone6sPlus: 46 | return Display(size: .screen5_5Inch, resolution: CGSize(width: 414, height: 736), physicalResolution: CGSize(width: 1080, height: 1920), renderedResolution: CGSize(width: 1242, height: 2208), scale: 3.0, density: 401, hasTrueTone: false, colorSpace: .sRGB) 47 | 48 | case .iPhone7Plus: 49 | return Display(size: .screen5_5Inch, resolution: CGSize(width: 414, height: 736), physicalResolution: CGSize(width: 1080, height: 1920), renderedResolution: CGSize(width: 1242, height: 2208), scale: 3.0, density: 401, hasTrueTone: false, colorSpace: .p3) 50 | 51 | case .iPhone8Plus: 52 | return Display(size: .screen5_5Inch, resolution: CGSize(width: 414, height: 736), physicalResolution: CGSize(width: 1080, height: 1920), renderedResolution: CGSize(width: 1242, height: 2208), scale: 3.0, density: 401, hasTrueTone: true, colorSpace: .p3) 53 | 54 | case .iPhoneX, .iPhoneXS, .iPhone11Pro: 55 | return Display(size: .screen5_8Inch, resolution: CGSize(width: 375, height: 812), physicalResolution: CGSize(width: 1125, height: 2436), renderedResolution: CGSize(width: 1125, height: 2436), scale: 3.0, density: 458, hasTrueTone: true, colorSpace: .p3) 56 | 57 | case .iPhoneXR, .iPhone11: 58 | return Display(size: .screen6_1Inch, resolution: CGSize(width: 414, height: 896), physicalResolution: CGSize(width: 828, height: 1792), renderedResolution: CGSize(width: 828, height: 1792), scale: 2.0, density: 326, hasTrueTone: true, colorSpace: .p3) 59 | 60 | case .iPhoneXSMax, .iPhone11ProMax: 61 | return Display(size: .screen6_5Inch, resolution: CGSize(width: 414, height: 896), physicalResolution: CGSize(width: 1242, height: 2688), renderedResolution: CGSize(width: 1242, height: 2688), scale: 3.0, density: 458, hasTrueTone: true, colorSpace: .p3) 62 | 63 | case .appleTV4: 64 | return Display(size: .notApplicable, resolution: CGSize(width: 1920, height: 1080), physicalResolution: CGSize(width: 1920, height: 1080), renderedResolution: CGSize(width: 1920, height: 1080), scale: 1.0, density: 0, hasTrueTone: false, colorSpace: .sRGB) 65 | 66 | case .appleTV4K: 67 | return Display(size: .notApplicable, resolution: CGSize(width: 3840, height: 2160), physicalResolution: CGSize(width: 3840, height: 2160), renderedResolution: CGSize(width: 3840, height: 2160), scale: 1.0, density: 0, hasTrueTone: false, colorSpace: .sRGB) 68 | 69 | case .iPad2: 70 | return Display(size: .screen9_7Inch, resolution: CGSize(width: 1024, height: 768), physicalResolution: CGSize(width: 1024, height: 768), renderedResolution: CGSize(width: 1024, height: 768), scale: 1.0, density: 132, hasTrueTone: false, colorSpace: .sRGB) 71 | 72 | case .iPad3, 73 | .iPad4, 74 | .iPad5, 75 | .iPad6, 76 | .iPadAir, 77 | .iPadAir2: 78 | return Display(size: .screen9_7Inch, resolution: CGSize(width: 1024, height: 768), physicalResolution: CGSize(width: 2048, height: 1536), renderedResolution: CGSize(width: 2048, height: 1536), scale: 2.0, density: 264, hasTrueTone: false, colorSpace: .sRGB) 79 | 80 | case .iPad7: 81 | return Display(size: .screen10_2Inch, resolution: CGSize(width: 1080, height: 810), physicalResolution: CGSize(width: 2160, height: 1620), renderedResolution: CGSize(width: 2160, height: 1620), scale: 2.0, density: 264, hasTrueTone: false, colorSpace: .sRGB) 82 | 83 | case .iPadPro12Inch: 84 | return Display(size: .screen12_9Inch, resolution: CGSize(width: 1366, height: 1024), physicalResolution: CGSize(width: 2732, height: 2048), renderedResolution: CGSize(width: 2732, height: 2048), scale: 2.0, density: 264, hasTrueTone: false, colorSpace: .sRGB) 85 | 86 | case .iPadPro9Inch: 87 | return Display(size: .screen9_7Inch, resolution: CGSize(width: 1024, height: 768), physicalResolution: CGSize(width: 2048, height: 1536), renderedResolution: CGSize(width: 2048, height: 1536), scale: 2.0, density: 264, hasTrueTone: true, colorSpace: .p3) 88 | 89 | case .iPadPro12Inch2G, 90 | .iPadPro12Inch3G: 91 | return Display(size: .screen12_9Inch, resolution: CGSize(width: 1366, height: 1024), physicalResolution: CGSize(width: 2732, height: 2048), renderedResolution: CGSize(width: 2732, height: 2048), scale: 2.0, density: 264, hasTrueTone: true, colorSpace: .p3) 92 | 93 | case .iPadPro10Inch, 94 | .iPadAir3: 95 | return Display(size: .screen10_5Inch, resolution: CGSize(width: 1112, height: 834), physicalResolution: CGSize(width: 2224, height: 1668), renderedResolution: CGSize(width: 2224, height: 1668), scale: 2.0, density: 264, hasTrueTone: true, colorSpace: .p3) 96 | 97 | case .iPadPro11Inch: 98 | return Display(size: .screen11Inch, resolution: CGSize(width: 1194, height: 834), physicalResolution: CGSize(width: 2388, height: 1668), renderedResolution: CGSize(width: 2388, height: 2668), scale: 2.0, density: 264, hasTrueTone: true, colorSpace: .p3) 99 | 100 | case .iPadMini: 101 | return Display(size: .screen7_9Inch, resolution: CGSize(width: 1024, height: 768), physicalResolution: CGSize(width: 1024, height: 768), renderedResolution: CGSize(width: 1024, height: 768), scale: 1.0, density: 163, hasTrueTone: false, colorSpace: .sRGB) 102 | 103 | case .iPadMini2, 104 | .iPadMini3, 105 | .iPadMini4: 106 | return Display(size: .screen7_9Inch, resolution: CGSize(width: 1024, height: 768), physicalResolution: CGSize(width: 2048, height: 1536), renderedResolution: CGSize(width: 2048, height: 1536), scale: 2.0, density: 326, hasTrueTone: false, colorSpace: .sRGB) 107 | 108 | case .iPadMini5: 109 | return Display(size: .screen7_9Inch, resolution: CGSize(width: 1024, height: 768), physicalResolution: CGSize(width: 2048, height: 1536), renderedResolution: CGSize(width: 2048, height: 1536), scale: 2.0, density: 326, hasTrueTone: false, colorSpace: .p3) 110 | 111 | case let .simulator(model): 112 | return model.display 113 | 114 | case .watch(.screen38mm), 115 | .watchSeries1(.screen38mm), 116 | .watchSeries2(.screen38mm), 117 | .watchSeries3(.screen38mm): 118 | return Display(size: .screen38mm, resolution: CGSize(width: 170, height: 136), physicalResolution: CGSize(width: 340, height: 272), renderedResolution: CGSize(width: 340, height: 272), scale: 2.0, density: 290, hasTrueTone: false, colorSpace: .sRGB) 119 | case .watch(.screen42mm), 120 | .watchSeries1(.screen42mm), 121 | .watchSeries2(.screen42mm), 122 | .watchSeries3(.screen42mm): 123 | return Display(size: .screen42mm, resolution: CGSize(width: 195, height: 156), physicalResolution: CGSize(width: 390, height: 312), renderedResolution: CGSize(width: 390, height: 312), scale: 2.0, density: 303, hasTrueTone: false, colorSpace: .sRGB) 124 | case .watchSeries4(.screen40mm), 125 | .watchSeries5(.screen40mm): 126 | return Display(size: .screen40mm, resolution: CGSize(width: 197, height: 162), physicalResolution: CGSize(width: 394, height: 324), renderedResolution: CGSize(width: 394, height: 324), scale: 2.0, density: 326, hasTrueTone: false, colorSpace: .sRGB) 127 | case .watchSeries4(.screen44mm), 128 | .watchSeries5(.screen44mm): 129 | return Display(size: .screen44mm, resolution: CGSize(width: 224, height: 184), physicalResolution: CGSize(width: 448, height: 368), renderedResolution: CGSize(width: 448, height: 368), scale: 2.0, density: 326, hasTrueTone: false, colorSpace: .sRGB) 130 | 131 | default: 132 | return Display(size: .notApplicable, resolution: CGSize.zero, physicalResolution: CGSize.zero, renderedResolution: CGSize.zero, scale: 0, density: 0, hasTrueTone: false, colorSpace: .sRGB) 133 | 134 | } 135 | } 136 | 137 | } 138 | -------------------------------------------------------------------------------- /Sources/Extensions/Device+Family.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Device 3 | // Thingy 4 | // 5 | // Created by Bojan Dimovski on 19/01/2020. 6 | // Copyright © 2020 Bojan Dimovski. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public extension Device { 12 | 13 | /// Associated family for each device. 14 | var family: Family { 15 | switch self { 16 | case .iPhone4S, 17 | .iPhone5, 18 | .iPhone5c, 19 | .iPhone5s, 20 | .iPhone6, 21 | .iPhone6Plus, 22 | .iPhone6s, 23 | .iPhone6sPlus, 24 | .iPhoneSE, 25 | .iPhoneSE2G, 26 | .iPhone7, 27 | .iPhone7Plus, 28 | .iPhone8, 29 | .iPhone8Plus, 30 | .iPhoneX, 31 | .iPhoneXR, 32 | .iPhoneXS, 33 | .iPhoneXSMax, 34 | .iPhone11, 35 | .iPhone11Pro, 36 | .iPhone11ProMax: 37 | return .phone 38 | 39 | case .iPodTouch5G, 40 | .iPodTouch6G, 41 | .iPodTouch7G: 42 | return .pod 43 | 44 | case .appleTV4, 45 | .appleTV4K: 46 | return .tv 47 | 48 | case .watch, 49 | .watchSeries1, 50 | .watchSeries2, 51 | .watchSeries3, 52 | .watchSeries4, 53 | .watchSeries5: 54 | return .watch 55 | 56 | case .iPad2, 57 | .iPad3, 58 | .iPad4, 59 | .iPad5, 60 | .iPad6, 61 | .iPad7, 62 | .iPadAir, 63 | .iPadAir2, 64 | .iPadAir3, 65 | .iPadPro12Inch, 66 | .iPadPro12Inch2G, 67 | .iPadPro12Inch3G, 68 | .iPadPro9Inch, 69 | .iPadPro10Inch, 70 | .iPadPro11Inch, 71 | .iPadMini, 72 | .iPadMini2, 73 | .iPadMini3, 74 | .iPadMini4, 75 | .iPadMini5: 76 | return .pad 77 | 78 | case let .simulator(model): 79 | return model.family 80 | 81 | case let .unknown(family): 82 | return family 83 | } 84 | } 85 | 86 | } 87 | -------------------------------------------------------------------------------- /Sources/Extensions/Device+MarketingName.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Device 3 | // Thingy 4 | // 5 | // Created by Bojan Dimovski on 19/01/2020. 6 | // Copyright © 2020 Bojan Dimovski. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | extension Device: MarketingProtocol { 12 | 13 | /// The full marketing name of the model, e.g. "iPhone 7 Plus", "Apple TV 4". 14 | public var marketingName: String { 15 | switch self { 16 | case .iPhone4S: 17 | return "iPhone 4S" 18 | case .iPhone5: 19 | return "iPhone 5" 20 | case .iPhone5s: 21 | return "iPhone 5s" 22 | case .iPhone5c: 23 | return "iPhone 5c" 24 | case .iPhone6: 25 | return "iPhone 6" 26 | case .iPhone6Plus: 27 | return "iPhone 6 Plus" 28 | case .iPhone6s: 29 | return "iPhone 6s" 30 | case .iPhone6sPlus: 31 | return "iPhone 6s Plus" 32 | case .iPhoneSE: 33 | return "iPhone SE" 34 | case .iPhoneSE2G: 35 | return "iPhone SE (2020)" 36 | case .iPhone7: 37 | return "iPhone 7" 38 | case .iPhone7Plus: 39 | return "iPhone 7 Plus" 40 | case .iPhone8: 41 | return "iPhone 8" 42 | case .iPhone8Plus: 43 | return "iPhone 8 Plus" 44 | case .iPhoneX: 45 | return "iPhone X" 46 | case .iPhoneXR: 47 | return "iPhone XR" 48 | case .iPhoneXS: 49 | return "iPhone XS" 50 | case .iPhoneXSMax: 51 | return "iPhone XS Max" 52 | case .iPhone11: 53 | return "iPhone 11" 54 | case .iPhone11Pro: 55 | return "iPhone 11 Pro" 56 | case .iPhone11ProMax: 57 | return "iPhone 11 Pro Max" 58 | 59 | case .iPad2: 60 | return "iPad 2" 61 | case .iPad3: 62 | return "iPad 3" 63 | case .iPad4: 64 | return "iPad 4" 65 | case .iPad5: 66 | return "iPad (2017)" 67 | case .iPad6: 68 | return "iPad (2018)" 69 | case .iPad7: 70 | return "iPad (2019)" 71 | case .iPadMini: 72 | return "iPad mini" 73 | case .iPadMini2: 74 | return "iPad mini 2" 75 | case .iPadMini3: 76 | return "iPad mini 3" 77 | case .iPadMini4: 78 | return "iPad mini 4" 79 | case .iPadMini5: 80 | return "iPad mini 5" 81 | case .iPadAir: 82 | return "iPad Air" 83 | case .iPadAir2: 84 | return "iPad Air 2" 85 | case .iPadAir3: 86 | return "iPad Air 3" 87 | case .iPadPro12Inch: 88 | return "12.9-inch iPad Pro" 89 | case .iPadPro9Inch: 90 | return "9.7-inch iPad Pro" 91 | case .iPadPro12Inch2G: 92 | return "12.9-inch iPad Pro (2nd generation)" 93 | case .iPadPro12Inch3G: 94 | return "12.9-inch iPad Pro (3rd generation)" 95 | case .iPadPro10Inch: 96 | return "10.5-inch iPad Pro" 97 | case .iPadPro11Inch: 98 | return "11-inch iPad Pro" 99 | 100 | case .iPodTouch5G: 101 | return "iPod touch (5th generation)" 102 | case .iPodTouch6G: 103 | return "iPod touch (6th generation)" 104 | case .iPodTouch7G: 105 | return "iPod touch (7th generation)" 106 | 107 | case .appleTV4: 108 | return "Apple TV" 109 | case .appleTV4K: 110 | return "Apple TV 4K" 111 | 112 | case .watch: 113 | return "Apple Watch" 114 | case .watchSeries1: 115 | return "Apple Watch Series 1" 116 | case .watchSeries2: 117 | return "Apple Watch Series 2" 118 | case .watchSeries3: 119 | return "Apple Watch Series 3" 120 | case .watchSeries4: 121 | return "Apple Watch Series 4" 122 | case .watchSeries5: 123 | return "Apple Watch Series 5" 124 | 125 | case let .simulator(model): 126 | return NSLocalizedString("Simulator (\(model.marketingName))", comment: "Simulator (\(model.marketingName))") 127 | 128 | case let .unknown(family): 129 | return NSLocalizedString("Unknown \(family.marketingName)", comment: "Unknown \(family.marketingName)") 130 | } 131 | 132 | } 133 | 134 | } 135 | -------------------------------------------------------------------------------- /Sources/Extensions/Device+ModelNumbers.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Device 3 | // Thingy 4 | // 5 | // Created by Bojan Dimovski on 19/01/2020. 6 | // Copyright © 2020 Bojan Dimovski. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | internal extension Device { 12 | 13 | var lowestNumber: Double { 14 | numbers.first ?? 0 15 | } 16 | 17 | /// Associated model numbers for each device. 18 | var numbers: [Double] { 19 | switch self { 20 | case .iPhone4S: 21 | return [4.1] 22 | case .iPhone5: 23 | return [5.1, 5.2] 24 | case .iPhone5c: 25 | return [5.3, 5.4] 26 | case .iPhone5s: 27 | return [6.1, 6.2] 28 | case .iPhone6: 29 | return [7.2] 30 | case .iPhone6Plus: 31 | return [7.1] 32 | case .iPhone6s: 33 | return [8.1] 34 | case .iPhone6sPlus: 35 | return [8.2] 36 | case .iPhoneSE: 37 | return [8.4] 38 | case .iPhoneSE2G: 39 | return [12.8] 40 | case .iPhone7: 41 | return [9.1, 9.3] 42 | case .iPhone7Plus: 43 | return [9.2, 9.4] 44 | case .iPhone8: 45 | return [10.1, 10.4] 46 | case .iPhone8Plus: 47 | return [10.2, 10.5] 48 | case .iPhoneX: 49 | return [10.3, 10.6] 50 | case .iPhoneXR: 51 | return [11.8] 52 | case .iPhoneXS: 53 | return [11.2] 54 | case .iPhoneXSMax: 55 | return [11.4, 11.6] 56 | case .iPhone11: 57 | return [12.1] 58 | case .iPhone11Pro: 59 | return [12.3] 60 | case .iPhone11ProMax: 61 | return [12.5] 62 | 63 | case .iPodTouch5G: 64 | return [5.1] 65 | case .iPodTouch6G: 66 | return [7.1] 67 | case .iPodTouch7G: 68 | return [9.1] 69 | 70 | case .appleTV4: 71 | return [5.3] 72 | case .appleTV4K: 73 | return [6.2] 74 | 75 | case .watch(.screen38mm): 76 | return [1.1] 77 | case .watch(.screen42mm): 78 | return [1.2] 79 | case .watchSeries1(.screen38mm): 80 | return [2.6] 81 | case .watchSeries1(.screen42mm): 82 | return [2.7] 83 | case .watchSeries2(.screen38mm): 84 | return [2.3] 85 | case .watchSeries2(.screen42mm): 86 | return [2.4] 87 | case .watchSeries3(.screen38mm): 88 | return [3.1, 3.3] 89 | case .watchSeries3(.screen42mm): 90 | return [3.2, 3.4] 91 | case .watchSeries4(.screen40mm): 92 | return [4.1, 4.3] 93 | case .watchSeries4(.screen44mm): 94 | return [4.2, 4.4] 95 | case .watchSeries5(.screen40mm): 96 | return [5.1, 5.3] 97 | case .watchSeries5(.screen44mm): 98 | return [5.2, 5.4] 99 | 100 | case .iPad2: 101 | return [2.1, 2.2, 2.3, 2.4] 102 | case .iPad3: 103 | return [3.1, 3.2, 3.3] 104 | case .iPad4: 105 | return [3.4, 3.5, 3.6] 106 | case .iPad5: 107 | return [6.11, 6.12] 108 | case .iPad6: 109 | return [7.5, 7.6] 110 | case .iPad7: 111 | return [7.11, 7.12] 112 | 113 | case .iPadAir: 114 | return [4.1, 4.2, 4.3] 115 | case .iPadAir2: 116 | return [5.3, 5.4] 117 | case .iPadAir3: 118 | return [11.3, 11.4] 119 | 120 | case .iPadPro12Inch: 121 | return [6.7, 6.8] 122 | case .iPadPro9Inch: 123 | return [6.3, 6.4] 124 | case .iPadPro12Inch2G: 125 | return [7.1, 7.2] 126 | case .iPadPro10Inch: 127 | return [7.3, 7.4] 128 | case .iPadPro12Inch3G: 129 | return [8.5, 8.6, 8.7, 8.8] 130 | case .iPadPro11Inch: 131 | return [8.1, 8.2, 8.3, 8.4] 132 | 133 | case .iPadMini: 134 | return [2.5, 2.6, 2.7] 135 | case .iPadMini2: 136 | return [4.4, 4.5, 4.6] 137 | case .iPadMini3: 138 | return [4.7, 4.8, 4.9] 139 | case .iPadMini4: 140 | return [5.1, 5.2] 141 | case .iPadMini5: 142 | return [11.1, 11.2] 143 | 144 | case let .simulator(model): 145 | return model.numbers 146 | 147 | default: 148 | return [0.0] 149 | } 150 | } 151 | 152 | } 153 | -------------------------------------------------------------------------------- /Sources/Extensions/Device+ProductLines.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Device 3 | // Thingy 4 | // 5 | // Created by Bojan Dimovski on 19/01/2020. 6 | // Copyright © 2020 Bojan Dimovski. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public extension Device { 12 | 13 | /// Product line of the model. 14 | var productLine: ProductLine? { 15 | switch family { 16 | case .pad: 17 | switch self { 18 | case .iPadAir, 19 | .iPadAir2, 20 | .iPadAir3: 21 | return Lines.iPad.air 22 | case .iPadPro12Inch, 23 | .iPadPro12Inch2G, 24 | .iPadPro12Inch3G, 25 | .iPadPro9Inch, 26 | .iPadPro10Inch, 27 | .iPadPro11Inch: 28 | return Lines.iPad.pro 29 | case .iPadMini, 30 | .iPadMini2, 31 | .iPadMini3, 32 | .iPadMini4, 33 | .iPadMini5: 34 | return Lines.iPad.mini 35 | default: 36 | return Lines.iPad.standard 37 | } 38 | case .phone: 39 | switch self { 40 | case .iPhoneSE, 41 | .iPhoneSE2G: 42 | return Lines.iPhone.se 43 | case .iPhoneX, 44 | .iPhoneXS, 45 | .iPhoneXSMax, 46 | .iPhone11Pro, 47 | .iPhone11ProMax: 48 | return Lines.iPhone.pro 49 | default: 50 | return Lines.iPhone.standard 51 | } 52 | default: 53 | return nil 54 | } 55 | } 56 | 57 | } 58 | -------------------------------------------------------------------------------- /Sources/Info-tvOS.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 3.0.2 19 | CFBundleVersion 20 | 10 21 | NSPrincipalClass 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /Sources/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 3.0.2 19 | CFBundleVersion 20 | 10 21 | NSPrincipalClass 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /Sources/Model/Display.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Display.swift 3 | // Thingy 4 | // 5 | // Created by Bojan Dimovski on 7/18/17. 6 | // Copyright © 2017 Bojan Dimovski. All rights reserved. 7 | // 8 | // DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE 9 | // Version 2, December 2004 10 | // 11 | // Copyright (C) 2004 Sam Hocevar 12 | // 13 | // Everyone is permitted to copy and distribute verbatim or modified 14 | // copies of this license document, and changing it is allowed as long 15 | // as the name is changed. 16 | // 17 | // DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE 18 | // TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 19 | // 20 | // 0. You just DO WHAT THE FUCK YOU WANT TO. 21 | // 22 | // 23 | 24 | import Foundation 25 | 26 | /// A struct that provides an extended info on the display. 27 | public struct Display: Equatable { 28 | 29 | /// An enum that describes the display size. 30 | public enum Size: Float { 31 | 32 | /// - screen38mm: 38mm Apple Watch screen. 33 | case screen38mm = 38 34 | 35 | /// - screen42mm: 42mm Apple Watch screen. 36 | case screen42mm = 42 37 | 38 | /// - screen40mm: 40mm Apple Watch screen. 39 | case screen40mm = 40 40 | 41 | /// - screen44mm: 44mm Apple Watch screen. 42 | case screen44mm = 44 43 | 44 | /// - screen3_5Inch: 3.5in iPhone screen. 45 | case screen3_5Inch = 3.5 46 | 47 | /// - screen4Inch: 4in iPhone/iPod touch screen. 48 | case screen4Inch = 4 49 | 50 | /// - screen4_7Inch: 4.7in iPhone screen. 51 | case screen4_7Inch = 4.7 52 | 53 | /// - screen5_5Inch: 5.5in iPhone screen. 54 | case screen5_5Inch = 5.5 55 | 56 | /// - screen5_8Inch: 5.8in iPhone screen. 57 | case screen5_8Inch = 5.8 58 | 59 | /// - screen6_1Inch: 6.1in iPhone screen. 60 | case screen6_1Inch = 6.1 61 | 62 | /// - screen6_5Inch: 6.5in iPhone screen. 63 | case screen6_5Inch = 6.5 64 | 65 | /// - screen7_9Inch: 7.9in iPad screen. 66 | case screen7_9Inch = 7.9 67 | 68 | /// - screen9_7Inch: 9.7in iPad screen. 69 | case screen9_7Inch = 9.7 70 | 71 | /// - screen10_5Inch: 10.2in iPad screen. 72 | case screen10_2Inch = 10.2 73 | 74 | /// - screen10_5Inch: 10.5in iPad screen. 75 | case screen10_5Inch = 10.5 76 | 77 | /// - screen10_5Inch: 10.5in iPad screen. 78 | case screen11Inch = 11 79 | 80 | /// - screen12_9Inch: 12.9in iPad screen. 81 | case screen12_9Inch = 12.9 82 | 83 | /// - notApplicable: Not applicable, in case of the Apple TV. 84 | case notApplicable = -1 85 | } 86 | 87 | /// An enum that describes all color spaces. 88 | public enum ColorSpace { 89 | /// - Wide color display (P3) 90 | case p3 91 | 92 | /// - Full sRGB standard 93 | case sRGB 94 | } 95 | 96 | /// Screen size in inches. 97 | public var size: Size 98 | 99 | /// Resolution of the device. 100 | public var resolution: CGSize 101 | 102 | /// Full physical resolution of the device, without any down-/up-sampling. 103 | public var physicalResolution: CGSize 104 | 105 | /// Rendered resolution of the device, with down-/up-sampling. 106 | public var renderedResolution: CGSize 107 | 108 | /// Screen scale, 1.0 for non-Retina devices. 109 | public var scale: Float 110 | 111 | /// Density of the display in PPI (pixels-per-inch). 112 | public var density: Int 113 | 114 | /// True Tone display. 115 | public var hasTrueTone: Bool 116 | 117 | /// Color space. 118 | public var colorSpace: ColorSpace 119 | 120 | } 121 | 122 | // MARK: - Screen size comparison 123 | 124 | extension Display.Size: Comparable { 125 | public static func <(lhs: Display.Size, rhs: Display.Size) -> Bool { 126 | lhs.rawValue < rhs.rawValue 127 | } 128 | 129 | public static func ==(lhs: Display.Size, rhs: Display.Size) -> Bool { 130 | lhs.rawValue == rhs.rawValue 131 | } 132 | } 133 | -------------------------------------------------------------------------------- /Sources/Model/Family.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Family 3 | // Thingy 4 | // 5 | // Created by Bojan Dimovski on 21.11.16. 6 | // Copyright © 2017 Bojan Dimovski. All rights reserved. 7 | // 8 | // DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE 9 | // Version 2, December 2004 10 | // 11 | // Copyright (C) 2004 Sam Hocevar 12 | // 13 | // Everyone is permitted to copy and distribute verbatim or modified 14 | // copies of this license document, and changing it is allowed as long 15 | // as the name is changed. 16 | // 17 | // DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE 18 | // TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 19 | // 20 | // 0. You just DO WHAT THE FUCK YOU WANT TO. 21 | // 22 | // 23 | 24 | import Foundation 25 | 26 | /// A type that describes the product family. 27 | 28 | public enum Family: String { 29 | /// - phone: iPhone product family. 30 | case phone = "iPhone" 31 | /// - pod: iPod touch product family. 32 | case pod = "iPod" 33 | /// - pad: iPad product family. 34 | case pad = "iPad" 35 | /// - tv: Apple TV product family. 36 | case tv = "AppleTV" 37 | /// - watch: Apple Watch product family. 38 | case watch = "Watch" 39 | } 40 | 41 | // MARK: - Marketing protocol 42 | 43 | extension Family: MarketingProtocol { 44 | 45 | /// The marketing name of the product family, e.g. "iPhone", "Apple TV". 46 | public var marketingName: String { 47 | switch self { 48 | case .phone: 49 | return "iPhone" 50 | case .pod: 51 | return "iPod" 52 | case .pad: 53 | return "iPad" 54 | case .tv: 55 | return "Apple TV" 56 | case .watch: 57 | return "Watch" 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /Sources/Model/Lines.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ProductLine 3 | // Thingy 4 | // 5 | // Created by Bojan Dimovski on 21.11.16. 6 | // Copyright © 2017 Bojan Dimovski. All rights reserved. 7 | // 8 | // DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE 9 | // Version 2, December 2004 10 | // 11 | // Copyright (C) 2004 Sam Hocevar 12 | // 13 | // Everyone is permitted to copy and distribute verbatim or modified 14 | // copies of this license document, and changing it is allowed as long 15 | // as the name is changed. 16 | // 17 | // DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE 18 | // TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 19 | // 20 | // 0. You just DO WHAT THE FUCK YOU WANT TO. 21 | // 22 | // 23 | 24 | import Foundation 25 | 26 | /// A struct containing all product lines. Currently iPad only. 27 | public enum Lines { 28 | 29 | /// A type that describes the specific iPad line. 30 | public enum iPad: ProductLine { 31 | /// - standard: A standard iPad product. 32 | case standard 33 | /// - pro: An iPad Pro product. 34 | case pro 35 | /// - mini: An iPad mini product. 36 | case mini 37 | /// - air: An iPad Air product. 38 | case air 39 | 40 | /// The marketing name of the iPad product line, e.g. "Pro", "Air". 41 | public var marketingName: String { 42 | switch self { 43 | case .pro: 44 | return "Pro" 45 | case .mini: 46 | return "mini" 47 | case .air: 48 | return "Air" 49 | default: 50 | return "" 51 | } 52 | } 53 | } 54 | 55 | /// A type that describes the specific iPhone line. 56 | public enum iPhone: ProductLine { 57 | /// - standard: A standard iPhone product. 58 | case standard 59 | /// - pro: An iPhone Pro product. 60 | case pro 61 | /// - se: An iPhone SE product. 62 | case se 63 | 64 | /// The marketing name of the iPhone product line, e.g. "Pro", "SE". 65 | public var marketingName: String { 66 | switch self { 67 | case .pro: 68 | return "Pro" 69 | case .se: 70 | return "SE" 71 | default: 72 | return "" 73 | } 74 | } 75 | } 76 | 77 | } 78 | -------------------------------------------------------------------------------- /Sources/Model/ThingyError.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ThingyError 3 | // Thingy 4 | // 5 | // Created by Bojan Dimovski on 19/01/2020. 6 | // Copyright © 2020 Bojan Dimovski. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | /// Errors thrown by Thingy. 12 | 13 | public enum ThingyError: Error { 14 | /// - An error thrown when devices of different families get compared. 15 | case incomparableFamilies 16 | /// - An error thrown when devices from different product lines get compared. 17 | case incomparableProductLines 18 | /// - An error thrown when an unknown device gets compared. 19 | case incomparableUnknownProduct 20 | } 21 | -------------------------------------------------------------------------------- /Sources/Protocols.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Protocols.swift 3 | // Thingy 4 | // 5 | // Created by Bojan Dimovski on 28.11.16. 6 | // Copyright © 2017 Bojan Dimovski. All rights reserved. 7 | // 8 | // DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE 9 | // Version 2, December 2004 10 | // 11 | // Copyright (C) 2004 Sam Hocevar 12 | // 13 | // Everyone is permitted to copy and distribute verbatim or modified 14 | // copies of this license document, and changing it is allowed as long 15 | // as the name is changed. 16 | // 17 | // DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE 18 | // TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 19 | // 20 | // 0. You just DO WHAT THE FUCK YOU WANT TO. 21 | // 22 | // 23 | 24 | import Foundation 25 | 26 | /// :nodoc: 27 | /// A protocol that describes products (lines and models). 28 | public protocol MarketingProtocol { 29 | 30 | /// The full marketing name of the entity. 31 | var marketingName: String { get } 32 | } 33 | 34 | // MARK: - Hashable and equatable protocols 35 | 36 | /// :nodoc: 37 | func ==(lhs: MarketingProtocol, rhs: MarketingProtocol) -> Bool { 38 | lhs.marketingName == rhs.marketingName 39 | } 40 | 41 | /// :nodoc: 42 | func !=(lhs: MarketingProtocol, rhs: MarketingProtocol) -> Bool { 43 | lhs.marketingName != rhs.marketingName 44 | } 45 | 46 | /// :nodoc: 47 | /// A dummy protocol used to encompass all product line enums. 48 | public typealias ProductLine = MarketingProtocol 49 | -------------------------------------------------------------------------------- /Sources/RawDevice.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RawDevice.swift 3 | // Thingy 4 | // 5 | // Created by Bojan Dimovski on 21.11.16. 6 | // Copyright © 2017 Bojan Dimovski. All rights reserved. 7 | // 8 | // DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE 9 | // Version 2, December 2004 10 | // 11 | // Copyright (C) 2004 Sam Hocevar 12 | // 13 | // Everyone is permitted to copy and distribute verbatim or modified 14 | // copies of this license document, and changing it is allowed as long 15 | // as the name is changed. 16 | // 17 | // DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE 18 | // TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 19 | // 20 | // 0. You just DO WHAT THE FUCK YOU WANT TO. 21 | // 22 | // 23 | 24 | import Foundation 25 | 26 | /// :nodoc: 27 | internal struct RawDevice { 28 | 29 | var major: Int = 0 30 | var minor: Int = 0 31 | var family: Family? 32 | var isSimulator = false 33 | private var _identifier: String? 34 | 35 | var modelNumber: Double { 36 | let factor: Double = minor < 10 ? 10 : 100 37 | return Double(major) + (Double(minor) / factor) 38 | } 39 | 40 | init(family: Family, modelNumber: Double) { 41 | self.init(identifier: "\(family.rawValue)\(modelNumber)") 42 | } 43 | 44 | init(identifier: String? = nil) { 45 | var identifier = identifier ?? self.identifier() 46 | 47 | isSimulator = (identifier == "x86_64" || identifier == "i386") 48 | 49 | if let simulatorIdentifier = ProcessInfo.processInfo.environment["SIMULATOR_MODEL_IDENTIFIER"], 50 | isSimulator { 51 | identifier = simulatorIdentifier 52 | } 53 | 54 | guard let regex = try? NSRegularExpression(pattern: "^([a-zA-Z]*)(\\d+)(\\,|\\.)(\\d+)$", options: [.caseInsensitive]) 55 | else { 56 | return 57 | } 58 | 59 | let range = NSRange(location: 0, length: identifier.count) 60 | let rawFamily = regex.stringByReplacingMatches(in: identifier, 61 | options: [], 62 | range: range, 63 | withTemplate: "$1") 64 | family = Family(rawValue: rawFamily) 65 | 66 | let modelString = regex.stringByReplacingMatches(in: identifier, 67 | options: [], 68 | range: range, 69 | withTemplate: "$2.$4") 70 | 71 | let modelComponents = modelString.components(separatedBy: ".") 72 | 73 | guard let majorString = modelComponents.first, 74 | let minorString = modelComponents.last 75 | else { 76 | return 77 | } 78 | 79 | major = Int(majorString) ?? 0 80 | minor = Int(minorString) ?? 0 81 | } 82 | 83 | internal mutating func identifier() -> String { 84 | if let _identifier = _identifier { 85 | return _identifier 86 | } 87 | 88 | var systemInfo = utsname() 89 | uname(&systemInfo) 90 | let machineMirror = Mirror(reflecting: systemInfo.machine) 91 | let identifier = machineMirror.children.reduce("") { identifier, element in 92 | guard let value = element.value as? Int8, 93 | value != 0 94 | else { 95 | return identifier 96 | } 97 | return identifier + String(UnicodeScalar(UInt8(value))) 98 | } 99 | 100 | _identifier = identifier 101 | return identifier 102 | } 103 | 104 | var device: Device? { 105 | guard let family = family 106 | else { 107 | return nil 108 | } 109 | 110 | let foundThingy = Device.allCases.filter { 111 | $0.numbers.contains(modelNumber) && $0.family == family 112 | }.first 113 | 114 | guard let thingy = foundThingy 115 | else { 116 | return .unknown(family) 117 | } 118 | 119 | guard !isSimulator 120 | else { 121 | return .simulator(thingy) 122 | } 123 | 124 | return thingy 125 | } 126 | 127 | } 128 | 129 | // MARK: - Comparison 130 | 131 | /// :nodoc: 132 | extension RawDevice: Comparable { 133 | 134 | public static func <(lhs: RawDevice, rhs: RawDevice) -> Bool { 135 | guard lhs.family == rhs.family 136 | else { 137 | return false 138 | } 139 | 140 | guard lhs.major >= rhs.major 141 | else { 142 | return true 143 | } 144 | 145 | return lhs.major == rhs.major && lhs.minor < rhs.minor 146 | } 147 | 148 | public static func ==(lhs: RawDevice, rhs: RawDevice) -> Bool { 149 | lhs.major == rhs.major && 150 | lhs.minor == rhs.minor && 151 | lhs.family == rhs.family 152 | } 153 | 154 | } 155 | -------------------------------------------------------------------------------- /Sources/Thingy.h: -------------------------------------------------------------------------------- 1 | // 2 | // Thingy.h 3 | // Thingy 4 | // 5 | // Created by Bojan Dimovski on 21.11.16. 6 | // Copyright © 2017 Bojan Dimovski. All rights reserved. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | 12 | //! Project version number for Thingy. 13 | FOUNDATION_EXPORT double ThingyVersionNumber; 14 | 15 | //! Project version string for Thingy. 16 | FOUNDATION_EXPORT const unsigned char ThingyVersionString[]; 17 | -------------------------------------------------------------------------------- /Tests/ComparisonTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ComparisonTests.swift 3 | // Thingy 4 | // 5 | // Created by Bojan Dimovski on 07.4.17. 6 | // Copyright (c) 2017 Bojan Dimovski. All rights reserved. 7 | // 8 | // DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE 9 | // Version 2, December 2004 10 | // 11 | // Copyright (C) 2004 Sam Hocevar 12 | // 13 | // Everyone is permitted to copy and distribute verbatim or modified 14 | // copies of this license document, and changing it is allowed as long 15 | // as the name is changed. 16 | // 17 | // DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE 18 | // TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 19 | // 20 | // 0. You just DO WHAT THE FUCK YOU WANT TO. 21 | // 22 | // 23 | 24 | import XCTest 25 | @testable import Thingy 26 | 27 | class ComparisonTests: XCTestCase { 28 | 29 | func testCompareDeviceIsEqual() { 30 | let device = Device(identifier: "iPad4,9") 31 | let comparisonModel = Device.iPadMini3 32 | 33 | XCTAssertNotNil(device) 34 | if let device = device { 35 | do { 36 | let result = try device.isEqual(to: comparisonModel) 37 | XCTAssert(result) 38 | } 39 | catch { 40 | XCTFail("The devices are incompatible.") 41 | } 42 | } 43 | } 44 | 45 | func testCompareDeviceIsNewer() { 46 | let device = Device.iPadMini4 47 | let comparisonModel = Device.iPadMini3 48 | 49 | do { 50 | let result = try device.isNewerOrEqual(than: comparisonModel) 51 | XCTAssert(result) 52 | } 53 | catch { 54 | XCTFail("The devices are incompatible.") 55 | } 56 | } 57 | 58 | func testCompareDeviceIsOlder() { 59 | let device = Device.iPadPro12Inch 60 | let comparisonModel = Device.iPadPro10Inch 61 | 62 | do { 63 | let result = try device.isOlder(than: comparisonModel) 64 | XCTAssert(result) 65 | } 66 | catch { 67 | XCTFail("The devices are incompatible.") 68 | } 69 | } 70 | 71 | func testCompareIncompatibleDevices() { 72 | var lhs = Device.unknown(.pad) 73 | var rhs = Device.iPadPro10Inch 74 | 75 | XCTAssertThrows(expression: try lhs.isEqual(to: rhs)) 76 | 77 | lhs = Device.iPadPro12Inch2G 78 | rhs = Device.unknown(.pad) 79 | 80 | XCTAssertThrows(expression: try lhs.isEqual(to: rhs)) 81 | 82 | lhs = Device.iPadPro12Inch2G 83 | rhs = Device.iPad5 84 | 85 | XCTAssertThrows(expression: try lhs.isEqual(to: rhs)) 86 | 87 | lhs = Device.iPhone7 88 | rhs = Device.appleTV4 89 | 90 | XCTAssertThrows(expression: try lhs.isEqual(to: rhs)) 91 | } 92 | 93 | func testCompareScreenSize() { 94 | var device1 = Device.iPadMini2 95 | var device2 = Device.iPadPro12Inch2G 96 | 97 | XCTAssertTrue(device1.displaySize < device2.displaySize) 98 | 99 | device1 = Device.iPadMini2 100 | device2 = Device.iPadMini4 101 | 102 | XCTAssertTrue(device1.displaySize <= device2.displaySize) 103 | 104 | device1 = Device.iPadPro10Inch 105 | device2 = Device.iPadPro9Inch 106 | 107 | XCTAssertFalse(device1.displaySize < device2.displaySize) 108 | 109 | device1 = Device.iPad3 110 | device2 = Device.iPad5 111 | 112 | XCTAssertTrue(device1.displaySize == device2.displaySize) 113 | 114 | device1 = Device.iPhoneX 115 | device2 = Device.iPhone4S 116 | 117 | XCTAssertTrue(device1.displaySize > device2.displaySize) 118 | 119 | device1 = Device.iPhone8Plus 120 | device2 = Device.iPhone8 121 | 122 | XCTAssertTrue(device1.displaySize >= device2.displaySize) 123 | 124 | device1 = Device.iPhone7 125 | device2 = Device.iPhone7Plus 126 | 127 | XCTAssertFalse(device1.displaySize > device2.displaySize) 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /Tests/DeviceTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DeviceTests.swift 3 | // Thingy 4 | // 5 | // Created by Bojan Dimovski on 21.11.16. 6 | // Copyright © 2017 Bojan Dimovski. All rights reserved. 7 | // 8 | // DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE 9 | // Version 2, December 2004 10 | // 11 | // Copyright (C) 2004 Sam Hocevar 12 | // 13 | // Everyone is permitted to copy and distribute verbatim or modified 14 | // copies of this license document, and changing it is allowed as long 15 | // as the name is changed. 16 | // 17 | // DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE 18 | // TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 19 | // 20 | // 0. You just DO WHAT THE FUCK YOU WANT TO. 21 | // 22 | // 23 | 24 | import XCTest 25 | @testable import Thingy 26 | 27 | class DeviceTests: XCTestCase { 28 | 29 | func testResolveValidIPad() throws { 30 | let sut = try XCTUnwrap(Device(identifier: "iPad8,8")) 31 | 32 | XCTAssertEqual(sut.marketingName, "12.9-inch iPad Pro (3rd generation)") 33 | XCTAssertEqual(sut.family, .pad) 34 | 35 | let productLine = try XCTUnwrap(sut.productLine) 36 | XCTAssertEqual(productLine.marketingName, Lines.iPad.pro.marketingName) 37 | } 38 | 39 | func testResolveValidAppleTV() throws { 40 | let sut = try XCTUnwrap(Device(identifier: "AppleTV6,2")) 41 | 42 | XCTAssertEqual(sut.marketingName, "Apple TV 4K") 43 | XCTAssertEqual(sut.family, .tv) 44 | XCTAssertNil(sut.productLine) 45 | } 46 | 47 | func testResolveValidIPhone() throws { 48 | let sut = try XCTUnwrap(Device(identifier: "iPhone12,8")) 49 | 50 | XCTAssertEqual(sut.marketingName, "iPhone SE (2020)") 51 | XCTAssertEqual(sut.family, .phone) 52 | 53 | let productLine = try XCTUnwrap(sut.productLine) 54 | XCTAssertEqual(productLine.marketingName, Lines.iPhone.se.marketingName) 55 | } 56 | 57 | func testResolveValidIPod() throws { 58 | let sut = try XCTUnwrap(Device(identifier: "iPod9,1")) 59 | 60 | XCTAssertEqual(sut.marketingName, "iPod touch (7th generation)") 61 | XCTAssertEqual(sut.family, .pod) 62 | XCTAssertNil(sut.productLine) 63 | } 64 | 65 | func testResolveValidWatch() throws { 66 | let sut = try XCTUnwrap(Device(identifier: "Watch5,1")) 67 | 68 | XCTAssertEqual(sut.marketingName, "Apple Watch Series 5") 69 | XCTAssertEqual(sut.family, .watch) 70 | XCTAssertNil(sut.productLine) 71 | } 72 | 73 | 74 | func testResolveFutureDevice() throws { 75 | let sut = try XCTUnwrap(Device(identifier: "iPad133,7")) 76 | 77 | XCTAssertEqual(sut.family, .pad) 78 | XCTAssertEqual(sut.marketingName, "Unknown iPad") 79 | } 80 | 81 | func testResolveInvalidDevice() { 82 | var sut = Device(identifier: "iPack133,7") 83 | 84 | XCTAssertNil(sut) 85 | 86 | sut = Device(identifier: "iPhone1337") 87 | XCTAssertNil(sut) 88 | } 89 | 90 | func testValidSimulator() throws { 91 | let sut = try XCTUnwrap(Device(identifier: "x86_64")) 92 | 93 | expectSimulator(actual: sut) { 94 | XCTAssertNotNil($0) 95 | } 96 | 97 | XCTAssertFalse(sut.marketingName.isEmpty) 98 | } 99 | 100 | func testAllDevices() throws { 101 | Device.allCases.forEach { device in 102 | XCTAssertNotEqual(device.display, Display(size: .notApplicable, resolution: CGSize.zero, physicalResolution: CGSize.zero, renderedResolution: CGSize.zero, scale: 0, density: 0, hasTrueTone: false, colorSpace: .sRGB)) 103 | XCTAssertNotEqual(device.numbers, [0.0]) 104 | } 105 | } 106 | 107 | 108 | func expectSimulator(actual: Device, file: StaticString = #file, line: UInt = #line, test: (Device?) -> Void) { 109 | guard case let .simulator(model) = actual 110 | else { 111 | XCTFail("Expected a simulator, got <\(actual)>", file: file, line: line) 112 | return 113 | } 114 | 115 | test(model) 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /Tests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 3.0.2 19 | CFBundleVersion 20 | 10 21 | 22 | 23 | -------------------------------------------------------------------------------- /Tests/RawDeviceTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RawDeviceTests 3 | // Thingy 4 | // 5 | // Created by Bojan Dimovski on 28.11.16. 6 | // Copyright © 2017 Bojan Dimovski. All rights reserved. 7 | // 8 | // DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE 9 | // Version 2, December 2004 10 | // 11 | // Copyright (C) 2004 Sam Hocevar 12 | // 13 | // Everyone is permitted to copy and distribute verbatim or modified 14 | // copies of this license document, and changing it is allowed as long 15 | // as the name is changed. 16 | // 17 | // DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE 18 | // TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 19 | // 20 | // 0. You just DO WHAT THE FUCK YOU WANT TO. 21 | // 22 | // 23 | 24 | import XCTest 25 | @testable import Thingy 26 | 27 | class RawDeviceTests: XCTestCase { 28 | func testParsingValidDevice() { 29 | var device = RawDevice(identifier: "iPad6,3") 30 | 31 | XCTAssertNotNil(device.family) 32 | if let family = device.family { 33 | XCTAssert(family == .pad) 34 | } 35 | 36 | XCTAssert(device.major == 6) 37 | XCTAssert(device.minor == 3) 38 | XCTAssertEqual(device.modelNumber, 6.3) 39 | 40 | device = RawDevice(identifier: "iPad10,10") 41 | XCTAssert(device.major == 10) 42 | XCTAssert(device.minor == 10) 43 | XCTAssertEqual(device.modelNumber, 10.10) 44 | } 45 | 46 | func testSimulatorDevice() { 47 | let device = RawDevice(identifier: nil) 48 | XCTAssertNotNil(device) 49 | XCTAssertTrue(device.isSimulator) 50 | } 51 | 52 | func testParsingInvalidDevice() { 53 | var device = RawDevice(identifier: "6,3") 54 | 55 | XCTAssertNil(device.family) 56 | XCTAssert(device.major == 6) 57 | XCTAssert(device.minor == 3) 58 | XCTAssertEqual(device.modelNumber, 6.3) 59 | 60 | device = RawDevice(identifier: "foo") 61 | 62 | XCTAssertNil(device.family) 63 | XCTAssert(device.major == 0) 64 | XCTAssert(device.minor == 0) 65 | XCTAssertEqual(device.modelNumber, 0) 66 | } 67 | 68 | func testParsingFutureDevice() { 69 | let identifier = "iPhone133,7" 70 | 71 | let rawDevice = RawDevice(identifier: identifier) 72 | 73 | XCTAssertNotNil(rawDevice.family) 74 | if let family = rawDevice.family { 75 | XCTAssert(family == .phone) 76 | } 77 | 78 | XCTAssert(rawDevice.major == 133) 79 | XCTAssert(rawDevice.minor == 7) 80 | } 81 | 82 | func testRawDeviceComparison() { 83 | XCTAssertFalse(RawDevice(family: .pad, modelNumber: 0.0) < RawDevice(family: .phone, modelNumber: 0.0)) 84 | XCTAssertTrue(RawDevice(family: .pad, modelNumber: 1.2) < RawDevice(family: .pad, modelNumber: 1.3)) 85 | } 86 | 87 | } 88 | -------------------------------------------------------------------------------- /Tests/XCTest+Throwable.swift: -------------------------------------------------------------------------------- 1 | // 2 | // XCTest+Throwable.swift 3 | // Thingy 4 | // 5 | // Created by Bojan Dimovski on 7/18/17. 6 | // Copyright © 2017 Bojan Dimovski. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import XCTest 11 | 12 | func XCTAssertThrows(expression: @autoclosure () throws -> T, _ message: String = "", file: StaticString = #file, line: UInt = #line) { 13 | do { 14 | _ = try expression() 15 | XCTFail("No error to catch! - \(message)", file: file, line: line) 16 | } 17 | catch { 18 | } 19 | } 20 | 21 | func XCTAssertNoThrow(expression: @autoclosure () throws -> T, _ message: String = "", file: StaticString = #file, line: UInt = #line) { 22 | do { 23 | _ = try expression() 24 | } 25 | catch let error { 26 | XCTFail("Caught error: \(error) - \(message)", file: file, line: line) 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /Thingy.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 324F2FD21F1E906000490671 /* XCTest+Throwable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 324F2FD11F1E906000490671 /* XCTest+Throwable.swift */; }; 11 | 324F2FD51F1E9DC600490671 /* Display.swift in Sources */ = {isa = PBXBuildFile; fileRef = 324F2FD41F1E9DC600490671 /* Display.swift */; }; 12 | 324F2FD61F1E9DC600490671 /* Display.swift in Sources */ = {isa = PBXBuildFile; fileRef = 324F2FD41F1E9DC600490671 /* Display.swift */; }; 13 | 324F2FD71F1E9DC600490671 /* Display.swift in Sources */ = {isa = PBXBuildFile; fileRef = 324F2FD41F1E9DC600490671 /* Display.swift */; }; 14 | 32D5B93D1DE354E500C6ED8C /* Thingy.h in Headers */ = {isa = PBXBuildFile; fileRef = AD88C459867230ACFFEF7EDB /* Thingy.h */; settings = {ATTRIBUTES = (Public, ); }; }; 15 | 32D5B93E1DE354E500C6ED8C /* Thingy.h in Headers */ = {isa = PBXBuildFile; fileRef = AD88C459867230ACFFEF7EDB /* Thingy.h */; settings = {ATTRIBUTES = (Public, ); }; }; 16 | 32D5B9401DE355B400C6ED8C /* Family.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32D5B93F1DE355B400C6ED8C /* Family.swift */; }; 17 | 32D5B9411DE355B400C6ED8C /* Family.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32D5B93F1DE355B400C6ED8C /* Family.swift */; }; 18 | 32D5B9421DE355B400C6ED8C /* Family.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32D5B93F1DE355B400C6ED8C /* Family.swift */; }; 19 | 32D5B9441DE355C200C6ED8C /* Lines.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32D5B9431DE355C200C6ED8C /* Lines.swift */; }; 20 | 32D5B9451DE355C200C6ED8C /* Lines.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32D5B9431DE355C200C6ED8C /* Lines.swift */; }; 21 | 32D5B9461DE355C200C6ED8C /* Lines.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32D5B9431DE355C200C6ED8C /* Lines.swift */; }; 22 | 32D5B9491DE356B000C6ED8C /* Device.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32D5B9481DE356B000C6ED8C /* Device.swift */; }; 23 | 32D5B94A1DE356B000C6ED8C /* Device.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32D5B9481DE356B000C6ED8C /* Device.swift */; }; 24 | 32D5B94B1DE356B000C6ED8C /* Device.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32D5B9481DE356B000C6ED8C /* Device.swift */; }; 25 | 32D5B9511DE35AFA00C6ED8C /* DeviceTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32D5B9501DE35AFA00C6ED8C /* DeviceTests.swift */; }; 26 | 32FAD8D11DECAA5E00AC5433 /* RawDevice.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32FAD8D01DECAA5E00AC5433 /* RawDevice.swift */; }; 27 | 32FAD8D31DECAB2B00AC5433 /* Protocols.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32FAD8D21DECAB2B00AC5433 /* Protocols.swift */; }; 28 | 32FAD8D41DECB24600AC5433 /* RawDeviceTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = AD88C813A58AD585E89B0A02 /* RawDeviceTests.swift */; }; 29 | 32FAD8DA1DECC12600AC5433 /* RawDevice.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32FAD8D01DECAA5E00AC5433 /* RawDevice.swift */; }; 30 | 32FAD8DB1DECC12600AC5433 /* Protocols.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32FAD8D21DECAB2B00AC5433 /* Protocols.swift */; }; 31 | 32FAD8DE1DECC12800AC5433 /* RawDevice.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32FAD8D01DECAA5E00AC5433 /* RawDevice.swift */; }; 32 | 32FAD8DF1DECC12800AC5433 /* Protocols.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32FAD8D21DECAB2B00AC5433 /* Protocols.swift */; }; 33 | 5990E09448DDAF7A9D209FB6 /* Device+Display.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5990EF3F995A74AA8C6CDCDC /* Device+Display.swift */; }; 34 | 5990E2D7138C52EAABB1D3F0 /* ThingyError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5990E3B16E40186E66B2C630 /* ThingyError.swift */; }; 35 | 5990E359CD1B63448846E304 /* Device+ModelNumbers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5990EF0427DA222A523063BA /* Device+ModelNumbers.swift */; }; 36 | 5990E387FBEF48C18BBAF85B /* Device+ModelNumbers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5990EF0427DA222A523063BA /* Device+ModelNumbers.swift */; }; 37 | 5990E3B9E85D58A623888436 /* Device+ProductLines.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5990E51C9AD972FDA9F0C509 /* Device+ProductLines.swift */; }; 38 | 5990E41E767043795E52F0A0 /* Device+ModelNumbers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5990EF0427DA222A523063BA /* Device+ModelNumbers.swift */; }; 39 | 5990E42EBB61949953F28B62 /* Device+Display.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5990EF3F995A74AA8C6CDCDC /* Device+Display.swift */; }; 40 | 5990E45EA49E5068DEAC2A84 /* Device+MarketingName.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5990E84EEEABB64CCBD5B5E2 /* Device+MarketingName.swift */; }; 41 | 5990E52A65BC429D877BF95D /* Device+Family.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5990E207861D80A82AC9B0C0 /* Device+Family.swift */; }; 42 | 5990E5906EA7C8F9DF609AA9 /* Device+Display.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5990EF3F995A74AA8C6CDCDC /* Device+Display.swift */; }; 43 | 5990E59B5068C3FD5873EBAD /* Device+Comparison.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5990E0AA0E08335F01B1489B /* Device+Comparison.swift */; }; 44 | 5990E5F6E10530C99375ECA2 /* Device+Comparison.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5990E0AA0E08335F01B1489B /* Device+Comparison.swift */; }; 45 | 5990E73B648208D1F29E58FC /* Device+Family.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5990E207861D80A82AC9B0C0 /* Device+Family.swift */; }; 46 | 5990E73FF56EE449752FCECA /* Device+MarketingName.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5990E84EEEABB64CCBD5B5E2 /* Device+MarketingName.swift */; }; 47 | 5990E764CFF17D54DCDC33EF /* ThingyError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5990E3B16E40186E66B2C630 /* ThingyError.swift */; }; 48 | 5990EB41D9049F5E9D89BEA1 /* Device+MarketingName.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5990E84EEEABB64CCBD5B5E2 /* Device+MarketingName.swift */; }; 49 | 5990EC14141B97065CC6AA42 /* Device+Family.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5990E207861D80A82AC9B0C0 /* Device+Family.swift */; }; 50 | 5990ED9518CA5B2D7F651F43 /* ThingyError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5990E3B16E40186E66B2C630 /* ThingyError.swift */; }; 51 | 5990EE5C1EB9B2909802947C /* Device+Comparison.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5990E0AA0E08335F01B1489B /* Device+Comparison.swift */; }; 52 | 5990EE8B25EF13E22A645195 /* Device+ProductLines.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5990E51C9AD972FDA9F0C509 /* Device+ProductLines.swift */; }; 53 | 5990EEBEBAB155AA32CECFD5 /* Device+ProductLines.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5990E51C9AD972FDA9F0C509 /* Device+ProductLines.swift */; }; 54 | AD88C194CCEBFDA00C7C44EA /* Thingy.h in Headers */ = {isa = PBXBuildFile; fileRef = AD88C459867230ACFFEF7EDB /* Thingy.h */; settings = {ATTRIBUTES = (Public, ); }; }; 55 | AD88C2DFE43E151C6FB20A10 /* Thingy.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = AD88C5F6C3AA1CADD3C7480E /* Thingy.framework */; }; 56 | AD88C7B050DA29CEE742C7D5 /* ComparisonTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = AD88C76C5D88B7C3890B6D9D /* ComparisonTests.swift */; }; 57 | /* End PBXBuildFile section */ 58 | 59 | /* Begin PBXContainerItemProxy section */ 60 | AD88C62ABEDCD78D36C919A2 /* PBXContainerItemProxy */ = { 61 | isa = PBXContainerItemProxy; 62 | containerPortal = AD88CDF9FD3BBF5B2C09AF5D /* Project object */; 63 | proxyType = 1; 64 | remoteGlobalIDString = AD88CC9CCE3163C7DBDA97EE; 65 | remoteInfo = Thingy; 66 | }; 67 | /* End PBXContainerItemProxy section */ 68 | 69 | /* Begin PBXFileReference section */ 70 | 324F2FD11F1E906000490671 /* XCTest+Throwable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "XCTest+Throwable.swift"; sourceTree = ""; }; 71 | 324F2FD41F1E9DC600490671 /* Display.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Display.swift; sourceTree = ""; }; 72 | 3282527D1DEE32220026BAD8 /* Info-tvOS.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "Info-tvOS.plist"; sourceTree = ""; }; 73 | 32D5B9281DE3534B00C6ED8C /* Thingy.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Thingy.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 74 | 32D5B9351DE3536900C6ED8C /* Thingy.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Thingy.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 75 | 32D5B93F1DE355B400C6ED8C /* Family.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Family.swift; sourceTree = ""; }; 76 | 32D5B9431DE355C200C6ED8C /* Lines.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Lines.swift; sourceTree = ""; }; 77 | 32D5B9481DE356B000C6ED8C /* Device.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Device.swift; sourceTree = ""; }; 78 | 32D5B9501DE35AFA00C6ED8C /* DeviceTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = DeviceTests.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; 79 | 32FAD8D01DECAA5E00AC5433 /* RawDevice.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RawDevice.swift; sourceTree = ""; }; 80 | 32FAD8D21DECAB2B00AC5433 /* Protocols.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Protocols.swift; sourceTree = ""; }; 81 | 34E242FD23CF44EB00CAB8CC /* Package.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Package.swift; sourceTree = ""; }; 82 | 5990E0AA0E08335F01B1489B /* Device+Comparison.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Device+Comparison.swift"; sourceTree = ""; }; 83 | 5990E207861D80A82AC9B0C0 /* Device+Family.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Device+Family.swift"; sourceTree = ""; }; 84 | 5990E3B16E40186E66B2C630 /* ThingyError.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ThingyError.swift; sourceTree = ""; }; 85 | 5990E51C9AD972FDA9F0C509 /* Device+ProductLines.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Device+ProductLines.swift"; sourceTree = ""; }; 86 | 5990E84EEEABB64CCBD5B5E2 /* Device+MarketingName.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Device+MarketingName.swift"; sourceTree = ""; }; 87 | 5990EF0427DA222A523063BA /* Device+ModelNumbers.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Device+ModelNumbers.swift"; sourceTree = ""; }; 88 | 5990EF3F995A74AA8C6CDCDC /* Device+Display.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Device+Display.swift"; sourceTree = ""; }; 89 | AD88C459867230ACFFEF7EDB /* Thingy.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = Thingy.h; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.objcpp; }; 90 | AD88C57407E90816F1ECEF58 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.info; path = Info.plist; sourceTree = ""; }; 91 | AD88C5F6C3AA1CADD3C7480E /* Thingy.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Thingy.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 92 | AD88C76C5D88B7C3890B6D9D /* ComparisonTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ComparisonTests.swift; sourceTree = ""; }; 93 | AD88C813A58AD585E89B0A02 /* RawDeviceTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RawDeviceTests.swift; sourceTree = ""; }; 94 | AD88CB78B87CDD5F9390F9F3 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.info; path = Info.plist; sourceTree = ""; }; 95 | AD88CEBBBC26647E231FF5DD /* Tests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = Tests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 96 | /* End PBXFileReference section */ 97 | 98 | /* Begin PBXFrameworksBuildPhase section */ 99 | 32D5B9241DE3534B00C6ED8C /* Frameworks */ = { 100 | isa = PBXFrameworksBuildPhase; 101 | buildActionMask = 2147483647; 102 | files = ( 103 | ); 104 | runOnlyForDeploymentPostprocessing = 0; 105 | }; 106 | 32D5B9311DE3536900C6ED8C /* Frameworks */ = { 107 | isa = PBXFrameworksBuildPhase; 108 | buildActionMask = 2147483647; 109 | files = ( 110 | ); 111 | runOnlyForDeploymentPostprocessing = 0; 112 | }; 113 | AD88C5EA754C99B2E92A2B51 /* Frameworks */ = { 114 | isa = PBXFrameworksBuildPhase; 115 | buildActionMask = 2147483647; 116 | files = ( 117 | AD88C2DFE43E151C6FB20A10 /* Thingy.framework in Frameworks */, 118 | ); 119 | runOnlyForDeploymentPostprocessing = 0; 120 | }; 121 | AD88CC0E30E8FED9682CB59A /* Frameworks */ = { 122 | isa = PBXFrameworksBuildPhase; 123 | buildActionMask = 2147483647; 124 | files = ( 125 | ); 126 | runOnlyForDeploymentPostprocessing = 0; 127 | }; 128 | /* End PBXFrameworksBuildPhase section */ 129 | 130 | /* Begin PBXGroup section */ 131 | 32D88E9D1DE3371E00E47A72 /* Supporting Files */ = { 132 | isa = PBXGroup; 133 | children = ( 134 | AD88C57407E90816F1ECEF58 /* Info.plist */, 135 | 3282527D1DEE32220026BAD8 /* Info-tvOS.plist */, 136 | AD88C459867230ACFFEF7EDB /* Thingy.h */, 137 | ); 138 | name = "Supporting Files"; 139 | sourceTree = ""; 140 | }; 141 | 32D88E9E1DE3375600E47A72 /* Supporting Files */ = { 142 | isa = PBXGroup; 143 | children = ( 144 | AD88CB78B87CDD5F9390F9F3 /* Info.plist */, 145 | 324F2FD11F1E906000490671 /* XCTest+Throwable.swift */, 146 | ); 147 | name = "Supporting Files"; 148 | sourceTree = ""; 149 | }; 150 | 34E242FE23CF465200CAB8CC /* Model */ = { 151 | isa = PBXGroup; 152 | children = ( 153 | 32D5B93F1DE355B400C6ED8C /* Family.swift */, 154 | 32D5B9431DE355C200C6ED8C /* Lines.swift */, 155 | 324F2FD41F1E9DC600490671 /* Display.swift */, 156 | 5990E3B16E40186E66B2C630 /* ThingyError.swift */, 157 | ); 158 | path = Model; 159 | sourceTree = ""; 160 | }; 161 | 5990E2B8F52650533406A388 /* Extensions */ = { 162 | isa = PBXGroup; 163 | children = ( 164 | 5990E207861D80A82AC9B0C0 /* Device+Family.swift */, 165 | 5990EF0427DA222A523063BA /* Device+ModelNumbers.swift */, 166 | 5990E51C9AD972FDA9F0C509 /* Device+ProductLines.swift */, 167 | 5990E84EEEABB64CCBD5B5E2 /* Device+MarketingName.swift */, 168 | 5990EF3F995A74AA8C6CDCDC /* Device+Display.swift */, 169 | 5990E0AA0E08335F01B1489B /* Device+Comparison.swift */, 170 | ); 171 | path = Extensions; 172 | sourceTree = ""; 173 | }; 174 | AD88C0F51310561142F7B7A7 = { 175 | isa = PBXGroup; 176 | children = ( 177 | 34E242FD23CF44EB00CAB8CC /* Package.swift */, 178 | AD88C8D3197EFA0401C39BF6 /* Sources */, 179 | AD88C57198FB176D7F8C8E0D /* Tests */, 180 | AD88C5746FC3A4D01C1C9136 /* Products */, 181 | ); 182 | sourceTree = ""; 183 | }; 184 | AD88C57198FB176D7F8C8E0D /* Tests */ = { 185 | isa = PBXGroup; 186 | children = ( 187 | 32D88E9E1DE3375600E47A72 /* Supporting Files */, 188 | 32D5B9501DE35AFA00C6ED8C /* DeviceTests.swift */, 189 | AD88C813A58AD585E89B0A02 /* RawDeviceTests.swift */, 190 | AD88C76C5D88B7C3890B6D9D /* ComparisonTests.swift */, 191 | ); 192 | path = Tests; 193 | sourceTree = ""; 194 | }; 195 | AD88C5746FC3A4D01C1C9136 /* Products */ = { 196 | isa = PBXGroup; 197 | children = ( 198 | AD88C5F6C3AA1CADD3C7480E /* Thingy.framework */, 199 | AD88CEBBBC26647E231FF5DD /* Tests.xctest */, 200 | 32D5B9281DE3534B00C6ED8C /* Thingy.framework */, 201 | 32D5B9351DE3536900C6ED8C /* Thingy.framework */, 202 | ); 203 | name = Products; 204 | sourceTree = ""; 205 | }; 206 | AD88C8D3197EFA0401C39BF6 /* Sources */ = { 207 | isa = PBXGroup; 208 | children = ( 209 | 32D5B9481DE356B000C6ED8C /* Device.swift */, 210 | 32FAD8D01DECAA5E00AC5433 /* RawDevice.swift */, 211 | 32FAD8D21DECAB2B00AC5433 /* Protocols.swift */, 212 | 5990E2B8F52650533406A388 /* Extensions */, 213 | 34E242FE23CF465200CAB8CC /* Model */, 214 | 32D88E9D1DE3371E00E47A72 /* Supporting Files */, 215 | ); 216 | path = Sources; 217 | sourceTree = ""; 218 | }; 219 | /* End PBXGroup section */ 220 | 221 | /* Begin PBXHeadersBuildPhase section */ 222 | 32D5B9251DE3534B00C6ED8C /* Headers */ = { 223 | isa = PBXHeadersBuildPhase; 224 | buildActionMask = 2147483647; 225 | files = ( 226 | 32D5B93E1DE354E500C6ED8C /* Thingy.h in Headers */, 227 | ); 228 | runOnlyForDeploymentPostprocessing = 0; 229 | }; 230 | 32D5B9321DE3536900C6ED8C /* Headers */ = { 231 | isa = PBXHeadersBuildPhase; 232 | buildActionMask = 2147483647; 233 | files = ( 234 | 32D5B93D1DE354E500C6ED8C /* Thingy.h in Headers */, 235 | ); 236 | runOnlyForDeploymentPostprocessing = 0; 237 | }; 238 | AD88CDEEE7144905C9FE95F2 /* Headers */ = { 239 | isa = PBXHeadersBuildPhase; 240 | buildActionMask = 2147483647; 241 | files = ( 242 | AD88C194CCEBFDA00C7C44EA /* Thingy.h in Headers */, 243 | ); 244 | runOnlyForDeploymentPostprocessing = 0; 245 | }; 246 | /* End PBXHeadersBuildPhase section */ 247 | 248 | /* Begin PBXNativeTarget section */ 249 | 32D5B9271DE3534B00C6ED8C /* Thingy watchOS */ = { 250 | isa = PBXNativeTarget; 251 | buildConfigurationList = 32D5B92D1DE3534C00C6ED8C /* Build configuration list for PBXNativeTarget "Thingy watchOS" */; 252 | buildPhases = ( 253 | 32D5B9231DE3534B00C6ED8C /* Sources */, 254 | 32D5B9241DE3534B00C6ED8C /* Frameworks */, 255 | 32D5B9251DE3534B00C6ED8C /* Headers */, 256 | 32D5B9261DE3534B00C6ED8C /* Resources */, 257 | ); 258 | buildRules = ( 259 | ); 260 | dependencies = ( 261 | ); 262 | name = "Thingy watchOS"; 263 | productName = "Thingy watchOS"; 264 | productReference = 32D5B9281DE3534B00C6ED8C /* Thingy.framework */; 265 | productType = "com.apple.product-type.framework"; 266 | }; 267 | 32D5B9341DE3536900C6ED8C /* Thingy tvOS */ = { 268 | isa = PBXNativeTarget; 269 | buildConfigurationList = 32D5B93A1DE3536900C6ED8C /* Build configuration list for PBXNativeTarget "Thingy tvOS" */; 270 | buildPhases = ( 271 | 32D5B9301DE3536900C6ED8C /* Sources */, 272 | 32D5B9311DE3536900C6ED8C /* Frameworks */, 273 | 32D5B9321DE3536900C6ED8C /* Headers */, 274 | 32D5B9331DE3536900C6ED8C /* Resources */, 275 | ); 276 | buildRules = ( 277 | ); 278 | dependencies = ( 279 | ); 280 | name = "Thingy tvOS"; 281 | productName = "Thingy tvOS"; 282 | productReference = 32D5B9351DE3536900C6ED8C /* Thingy.framework */; 283 | productType = "com.apple.product-type.framework"; 284 | }; 285 | AD88C67C53FE4E578B02552D /* Tests */ = { 286 | isa = PBXNativeTarget; 287 | buildConfigurationList = AD88C5FB5FC75605B8C40FDA /* Build configuration list for PBXNativeTarget "Tests" */; 288 | buildPhases = ( 289 | AD88C8AB04A2246C1769E022 /* Sources */, 290 | AD88C5EA754C99B2E92A2B51 /* Frameworks */, 291 | AD88C00DE38088E6EEFC58B6 /* Resources */, 292 | ); 293 | buildRules = ( 294 | ); 295 | dependencies = ( 296 | AD88C352087385CEF3C5AABB /* PBXTargetDependency */, 297 | ); 298 | name = Tests; 299 | productName = DeviceTests; 300 | productReference = AD88CEBBBC26647E231FF5DD /* Tests.xctest */; 301 | productType = "com.apple.product-type.bundle.unit-test"; 302 | }; 303 | AD88CC9CCE3163C7DBDA97EE /* Thingy iOS */ = { 304 | isa = PBXNativeTarget; 305 | buildConfigurationList = AD88C620B3BDE5FD2AEDC8A8 /* Build configuration list for PBXNativeTarget "Thingy iOS" */; 306 | buildPhases = ( 307 | AD88CB490D376D7C7FA589DE /* Sources */, 308 | AD88CC0E30E8FED9682CB59A /* Frameworks */, 309 | AD88CDEEE7144905C9FE95F2 /* Headers */, 310 | AD88CB6853ACC9A0EE38391F /* Resources */, 311 | ); 312 | buildRules = ( 313 | ); 314 | dependencies = ( 315 | ); 316 | name = "Thingy iOS"; 317 | productName = "Thingy iOS"; 318 | productReference = AD88C5F6C3AA1CADD3C7480E /* Thingy.framework */; 319 | productType = "com.apple.product-type.framework"; 320 | }; 321 | /* End PBXNativeTarget section */ 322 | 323 | /* Begin PBXProject section */ 324 | AD88CDF9FD3BBF5B2C09AF5D /* Project object */ = { 325 | isa = PBXProject; 326 | attributes = { 327 | LastSwiftUpdateCheck = 0810; 328 | LastUpgradeCheck = 1020; 329 | ORGANIZATIONNAME = "Bojan Dimovski"; 330 | TargetAttributes = { 331 | 32D5B9271DE3534B00C6ED8C = { 332 | CreatedOnToolsVersion = 8.1; 333 | LastSwiftMigration = 0810; 334 | ProvisioningStyle = Automatic; 335 | }; 336 | 32D5B9341DE3536900C6ED8C = { 337 | CreatedOnToolsVersion = 8.1; 338 | LastSwiftMigration = 0810; 339 | ProvisioningStyle = Automatic; 340 | }; 341 | AD88C67C53FE4E578B02552D = { 342 | LastSwiftMigration = 1020; 343 | }; 344 | AD88CC9CCE3163C7DBDA97EE = { 345 | LastSwiftMigration = 1020; 346 | ProvisioningStyle = Automatic; 347 | }; 348 | }; 349 | }; 350 | buildConfigurationList = AD88CB75C26FB3DF1A51E108 /* Build configuration list for PBXProject "Thingy" */; 351 | compatibilityVersion = "Xcode 3.2"; 352 | developmentRegion = en; 353 | hasScannedForEncodings = 0; 354 | knownRegions = ( 355 | en, 356 | Base, 357 | ); 358 | mainGroup = AD88C0F51310561142F7B7A7; 359 | productRefGroup = AD88C5746FC3A4D01C1C9136 /* Products */; 360 | projectDirPath = ""; 361 | projectRoot = ""; 362 | targets = ( 363 | AD88CC9CCE3163C7DBDA97EE /* Thingy iOS */, 364 | 32D5B9271DE3534B00C6ED8C /* Thingy watchOS */, 365 | 32D5B9341DE3536900C6ED8C /* Thingy tvOS */, 366 | AD88C67C53FE4E578B02552D /* Tests */, 367 | ); 368 | }; 369 | /* End PBXProject section */ 370 | 371 | /* Begin PBXResourcesBuildPhase section */ 372 | 32D5B9261DE3534B00C6ED8C /* Resources */ = { 373 | isa = PBXResourcesBuildPhase; 374 | buildActionMask = 2147483647; 375 | files = ( 376 | ); 377 | runOnlyForDeploymentPostprocessing = 0; 378 | }; 379 | 32D5B9331DE3536900C6ED8C /* Resources */ = { 380 | isa = PBXResourcesBuildPhase; 381 | buildActionMask = 2147483647; 382 | files = ( 383 | ); 384 | runOnlyForDeploymentPostprocessing = 0; 385 | }; 386 | AD88C00DE38088E6EEFC58B6 /* Resources */ = { 387 | isa = PBXResourcesBuildPhase; 388 | buildActionMask = 2147483647; 389 | files = ( 390 | ); 391 | runOnlyForDeploymentPostprocessing = 0; 392 | }; 393 | AD88CB6853ACC9A0EE38391F /* Resources */ = { 394 | isa = PBXResourcesBuildPhase; 395 | buildActionMask = 2147483647; 396 | files = ( 397 | ); 398 | runOnlyForDeploymentPostprocessing = 0; 399 | }; 400 | /* End PBXResourcesBuildPhase section */ 401 | 402 | /* Begin PBXSourcesBuildPhase section */ 403 | 32D5B9231DE3534B00C6ED8C /* Sources */ = { 404 | isa = PBXSourcesBuildPhase; 405 | buildActionMask = 2147483647; 406 | files = ( 407 | 32D5B94A1DE356B000C6ED8C /* Device.swift in Sources */, 408 | 32D5B9411DE355B400C6ED8C /* Family.swift in Sources */, 409 | 32FAD8DF1DECC12800AC5433 /* Protocols.swift in Sources */, 410 | 324F2FD61F1E9DC600490671 /* Display.swift in Sources */, 411 | 32FAD8DE1DECC12800AC5433 /* RawDevice.swift in Sources */, 412 | 32D5B9451DE355C200C6ED8C /* Lines.swift in Sources */, 413 | 5990E52A65BC429D877BF95D /* Device+Family.swift in Sources */, 414 | 5990E387FBEF48C18BBAF85B /* Device+ModelNumbers.swift in Sources */, 415 | 5990EE8B25EF13E22A645195 /* Device+ProductLines.swift in Sources */, 416 | 5990EB41D9049F5E9D89BEA1 /* Device+MarketingName.swift in Sources */, 417 | 5990E09448DDAF7A9D209FB6 /* Device+Display.swift in Sources */, 418 | 5990E5F6E10530C99375ECA2 /* Device+Comparison.swift in Sources */, 419 | 5990E2D7138C52EAABB1D3F0 /* ThingyError.swift in Sources */, 420 | ); 421 | runOnlyForDeploymentPostprocessing = 0; 422 | }; 423 | 32D5B9301DE3536900C6ED8C /* Sources */ = { 424 | isa = PBXSourcesBuildPhase; 425 | buildActionMask = 2147483647; 426 | files = ( 427 | 32D5B94B1DE356B000C6ED8C /* Device.swift in Sources */, 428 | 32D5B9421DE355B400C6ED8C /* Family.swift in Sources */, 429 | 32FAD8DB1DECC12600AC5433 /* Protocols.swift in Sources */, 430 | 324F2FD71F1E9DC600490671 /* Display.swift in Sources */, 431 | 32FAD8DA1DECC12600AC5433 /* RawDevice.swift in Sources */, 432 | 32D5B9461DE355C200C6ED8C /* Lines.swift in Sources */, 433 | 5990EC14141B97065CC6AA42 /* Device+Family.swift in Sources */, 434 | 5990E41E767043795E52F0A0 /* Device+ModelNumbers.swift in Sources */, 435 | 5990E3B9E85D58A623888436 /* Device+ProductLines.swift in Sources */, 436 | 5990E45EA49E5068DEAC2A84 /* Device+MarketingName.swift in Sources */, 437 | 5990E5906EA7C8F9DF609AA9 /* Device+Display.swift in Sources */, 438 | 5990E59B5068C3FD5873EBAD /* Device+Comparison.swift in Sources */, 439 | 5990ED9518CA5B2D7F651F43 /* ThingyError.swift in Sources */, 440 | ); 441 | runOnlyForDeploymentPostprocessing = 0; 442 | }; 443 | AD88C8AB04A2246C1769E022 /* Sources */ = { 444 | isa = PBXSourcesBuildPhase; 445 | buildActionMask = 2147483647; 446 | files = ( 447 | 32FAD8D41DECB24600AC5433 /* RawDeviceTests.swift in Sources */, 448 | 32D5B9511DE35AFA00C6ED8C /* DeviceTests.swift in Sources */, 449 | 324F2FD21F1E906000490671 /* XCTest+Throwable.swift in Sources */, 450 | AD88C7B050DA29CEE742C7D5 /* ComparisonTests.swift in Sources */, 451 | ); 452 | runOnlyForDeploymentPostprocessing = 0; 453 | }; 454 | AD88CB490D376D7C7FA589DE /* Sources */ = { 455 | isa = PBXSourcesBuildPhase; 456 | buildActionMask = 2147483647; 457 | files = ( 458 | 32D5B9491DE356B000C6ED8C /* Device.swift in Sources */, 459 | 32D5B9401DE355B400C6ED8C /* Family.swift in Sources */, 460 | 32FAD8D31DECAB2B00AC5433 /* Protocols.swift in Sources */, 461 | 324F2FD51F1E9DC600490671 /* Display.swift in Sources */, 462 | 32FAD8D11DECAA5E00AC5433 /* RawDevice.swift in Sources */, 463 | 32D5B9441DE355C200C6ED8C /* Lines.swift in Sources */, 464 | 5990E73B648208D1F29E58FC /* Device+Family.swift in Sources */, 465 | 5990E359CD1B63448846E304 /* Device+ModelNumbers.swift in Sources */, 466 | 5990EEBEBAB155AA32CECFD5 /* Device+ProductLines.swift in Sources */, 467 | 5990E73FF56EE449752FCECA /* Device+MarketingName.swift in Sources */, 468 | 5990E42EBB61949953F28B62 /* Device+Display.swift in Sources */, 469 | 5990EE5C1EB9B2909802947C /* Device+Comparison.swift in Sources */, 470 | 5990E764CFF17D54DCDC33EF /* ThingyError.swift in Sources */, 471 | ); 472 | runOnlyForDeploymentPostprocessing = 0; 473 | }; 474 | /* End PBXSourcesBuildPhase section */ 475 | 476 | /* Begin PBXTargetDependency section */ 477 | AD88C352087385CEF3C5AABB /* PBXTargetDependency */ = { 478 | isa = PBXTargetDependency; 479 | target = AD88CC9CCE3163C7DBDA97EE /* Thingy iOS */; 480 | targetProxy = AD88C62ABEDCD78D36C919A2 /* PBXContainerItemProxy */; 481 | }; 482 | /* End PBXTargetDependency section */ 483 | 484 | /* Begin XCBuildConfiguration section */ 485 | 32D5B92E1DE3534C00C6ED8C /* Debug */ = { 486 | isa = XCBuildConfiguration; 487 | buildSettings = { 488 | ALWAYS_SEARCH_USER_PATHS = NO; 489 | APPLICATION_EXTENSION_API_ONLY = YES; 490 | CLANG_ENABLE_MODULES = YES; 491 | CLANG_ENABLE_OBJC_WEAK = YES; 492 | CODE_SIGN_IDENTITY = ""; 493 | DEFINES_MODULE = YES; 494 | DYLIB_COMPATIBILITY_VERSION = 1; 495 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 496 | INFOPLIST_FILE = Sources/Info.plist; 497 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 498 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 499 | PRODUCT_BUNDLE_IDENTIFIER = com.bojandimovski.Thingy; 500 | SDKROOT = watchos; 501 | SKIP_INSTALL = YES; 502 | SUPPORTED_PLATFORMS = "watchsimulator watchos"; 503 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 504 | SWIFT_VERSION = 5.0; 505 | TARGETED_DEVICE_FAMILY = 4; 506 | WATCHOS_DEPLOYMENT_TARGET = 2.0; 507 | }; 508 | name = Debug; 509 | }; 510 | 32D5B92F1DE3534C00C6ED8C /* Release */ = { 511 | isa = XCBuildConfiguration; 512 | buildSettings = { 513 | ALWAYS_SEARCH_USER_PATHS = NO; 514 | APPLICATION_EXTENSION_API_ONLY = YES; 515 | CLANG_ENABLE_MODULES = YES; 516 | CLANG_ENABLE_OBJC_WEAK = YES; 517 | CODE_SIGN_IDENTITY = ""; 518 | DEFINES_MODULE = YES; 519 | DYLIB_COMPATIBILITY_VERSION = 1; 520 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 521 | INFOPLIST_FILE = Sources/Info.plist; 522 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 523 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 524 | PRODUCT_BUNDLE_IDENTIFIER = com.bojandimovski.Thingy; 525 | SDKROOT = watchos; 526 | SKIP_INSTALL = YES; 527 | SUPPORTED_PLATFORMS = "watchsimulator watchos"; 528 | SWIFT_VERSION = 5.0; 529 | TARGETED_DEVICE_FAMILY = 4; 530 | WATCHOS_DEPLOYMENT_TARGET = 2.0; 531 | }; 532 | name = Release; 533 | }; 534 | 32D5B93B1DE3536900C6ED8C /* Debug */ = { 535 | isa = XCBuildConfiguration; 536 | buildSettings = { 537 | ALWAYS_SEARCH_USER_PATHS = NO; 538 | CLANG_ENABLE_MODULES = YES; 539 | CLANG_ENABLE_OBJC_WEAK = YES; 540 | CODE_SIGN_IDENTITY = ""; 541 | DEFINES_MODULE = YES; 542 | DYLIB_COMPATIBILITY_VERSION = 1; 543 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 544 | INFOPLIST_FILE = "Sources/Info-tvOS.plist"; 545 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 546 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 547 | PRODUCT_BUNDLE_IDENTIFIER = com.bojandimovski.Thingy; 548 | SDKROOT = appletvos; 549 | SKIP_INSTALL = YES; 550 | SUPPORTED_PLATFORMS = "appletvsimulator appletvos"; 551 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 552 | SWIFT_VERSION = 5.0; 553 | TARGETED_DEVICE_FAMILY = 3; 554 | TVOS_DEPLOYMENT_TARGET = 9.0; 555 | }; 556 | name = Debug; 557 | }; 558 | 32D5B93C1DE3536900C6ED8C /* Release */ = { 559 | isa = XCBuildConfiguration; 560 | buildSettings = { 561 | ALWAYS_SEARCH_USER_PATHS = NO; 562 | CLANG_ENABLE_MODULES = YES; 563 | CLANG_ENABLE_OBJC_WEAK = YES; 564 | CODE_SIGN_IDENTITY = ""; 565 | DEFINES_MODULE = YES; 566 | DYLIB_COMPATIBILITY_VERSION = 1; 567 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 568 | INFOPLIST_FILE = "Sources/Info-tvOS.plist"; 569 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 570 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 571 | PRODUCT_BUNDLE_IDENTIFIER = com.bojandimovski.Thingy; 572 | SDKROOT = appletvos; 573 | SKIP_INSTALL = YES; 574 | SUPPORTED_PLATFORMS = "appletvsimulator appletvos"; 575 | SWIFT_VERSION = 5.0; 576 | TARGETED_DEVICE_FAMILY = 3; 577 | TVOS_DEPLOYMENT_TARGET = 9.0; 578 | }; 579 | name = Release; 580 | }; 581 | AD88C01082EF1EE75F31C9E6 /* Debug */ = { 582 | isa = XCBuildConfiguration; 583 | buildSettings = { 584 | ALWAYS_SEARCH_USER_PATHS = NO; 585 | CLANG_ENABLE_MODULES = YES; 586 | CLANG_ENABLE_OBJC_WEAK = YES; 587 | CODE_SIGN_IDENTITY = ""; 588 | DEFINES_MODULE = YES; 589 | DYLIB_COMPATIBILITY_VERSION = 1; 590 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 591 | INFOPLIST_FILE = Sources/Info.plist; 592 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 593 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 594 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 595 | PRODUCT_BUNDLE_IDENTIFIER = com.bojandimovski.Thingy; 596 | SDKROOT = iphoneos; 597 | SKIP_INSTALL = YES; 598 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 599 | SWIFT_VERSION = 5.0; 600 | TARGETED_DEVICE_FAMILY = "1,2"; 601 | }; 602 | name = Debug; 603 | }; 604 | AD88C147F31784D254F85D98 /* Release */ = { 605 | isa = XCBuildConfiguration; 606 | buildSettings = { 607 | ALWAYS_SEARCH_USER_PATHS = NO; 608 | CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; 609 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 610 | CLANG_WARN_BOOL_CONVERSION = YES; 611 | CLANG_WARN_COMMA = YES; 612 | CLANG_WARN_CONSTANT_CONVERSION = YES; 613 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 614 | CLANG_WARN_EMPTY_BODY = YES; 615 | CLANG_WARN_ENUM_CONVERSION = YES; 616 | CLANG_WARN_INFINITE_RECURSION = YES; 617 | CLANG_WARN_INT_CONVERSION = YES; 618 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 619 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 620 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 621 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 622 | CLANG_WARN_STRICT_PROTOTYPES = YES; 623 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 624 | CLANG_WARN_UNREACHABLE_CODE = YES; 625 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 626 | CURRENT_PROJECT_VERSION = 10; 627 | DYLIB_CURRENT_VERSION = 10; 628 | ENABLE_STRICT_OBJC_MSGSEND = YES; 629 | ENABLE_TESTABILITY = YES; 630 | GCC_NO_COMMON_BLOCKS = YES; 631 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 632 | GCC_WARN_ABOUT_RETURN_TYPE = YES; 633 | GCC_WARN_UNDECLARED_SELECTOR = YES; 634 | GCC_WARN_UNINITIALIZED_AUTOS = YES; 635 | GCC_WARN_UNUSED_FUNCTION = YES; 636 | GCC_WARN_UNUSED_VARIABLE = YES; 637 | PRODUCT_NAME = Thingy; 638 | SUPPORTED_PLATFORMS = "iphonesimulator iphoneos"; 639 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 640 | }; 641 | name = Release; 642 | }; 643 | AD88C352FCCA2C20DD77D4A9 /* Release */ = { 644 | isa = XCBuildConfiguration; 645 | buildSettings = { 646 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 647 | CLANG_ENABLE_MODULES = YES; 648 | CLANG_ENABLE_OBJC_WEAK = YES; 649 | CODE_SIGN_IDENTITY = "iPhone Developer"; 650 | INFOPLIST_FILE = Tests/Info.plist; 651 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 652 | PRODUCT_BUNDLE_IDENTIFIER = "com.bojandimovski.ThingyTests-iOS"; 653 | PRODUCT_NAME = "$(TARGET_NAME)"; 654 | SDKROOT = iphoneos; 655 | SWIFT_VERSION = 5.0; 656 | }; 657 | name = Release; 658 | }; 659 | AD88C55B31FDA3A3AE9EEFFE /* Debug */ = { 660 | isa = XCBuildConfiguration; 661 | buildSettings = { 662 | ALWAYS_SEARCH_USER_PATHS = NO; 663 | CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; 664 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 665 | CLANG_WARN_BOOL_CONVERSION = YES; 666 | CLANG_WARN_COMMA = YES; 667 | CLANG_WARN_CONSTANT_CONVERSION = YES; 668 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 669 | CLANG_WARN_EMPTY_BODY = YES; 670 | CLANG_WARN_ENUM_CONVERSION = YES; 671 | CLANG_WARN_INFINITE_RECURSION = YES; 672 | CLANG_WARN_INT_CONVERSION = YES; 673 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 674 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 675 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 676 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 677 | CLANG_WARN_STRICT_PROTOTYPES = YES; 678 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 679 | CLANG_WARN_UNREACHABLE_CODE = YES; 680 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 681 | CURRENT_PROJECT_VERSION = 10; 682 | DYLIB_CURRENT_VERSION = 10; 683 | ENABLE_STRICT_OBJC_MSGSEND = YES; 684 | ENABLE_TESTABILITY = YES; 685 | GCC_NO_COMMON_BLOCKS = YES; 686 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 687 | GCC_WARN_ABOUT_RETURN_TYPE = YES; 688 | GCC_WARN_UNDECLARED_SELECTOR = YES; 689 | GCC_WARN_UNINITIALIZED_AUTOS = YES; 690 | GCC_WARN_UNUSED_FUNCTION = YES; 691 | GCC_WARN_UNUSED_VARIABLE = YES; 692 | ONLY_ACTIVE_ARCH = YES; 693 | PRODUCT_NAME = Thingy; 694 | SUPPORTED_PLATFORMS = "iphonesimulator iphoneos"; 695 | }; 696 | name = Debug; 697 | }; 698 | AD88CA0DED769975DBE9DD62 /* Release */ = { 699 | isa = XCBuildConfiguration; 700 | buildSettings = { 701 | ALWAYS_SEARCH_USER_PATHS = NO; 702 | CLANG_ENABLE_MODULES = YES; 703 | CLANG_ENABLE_OBJC_WEAK = YES; 704 | CODE_SIGN_IDENTITY = ""; 705 | DEFINES_MODULE = YES; 706 | DYLIB_COMPATIBILITY_VERSION = 1; 707 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 708 | INFOPLIST_FILE = Sources/Info.plist; 709 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 710 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 711 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 712 | PRODUCT_BUNDLE_IDENTIFIER = com.bojandimovski.Thingy; 713 | SDKROOT = iphoneos; 714 | SKIP_INSTALL = YES; 715 | SWIFT_VERSION = 5.0; 716 | TARGETED_DEVICE_FAMILY = "1,2"; 717 | }; 718 | name = Release; 719 | }; 720 | AD88CE3C2205A05E57F5E50A /* Debug */ = { 721 | isa = XCBuildConfiguration; 722 | buildSettings = { 723 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 724 | CLANG_ENABLE_MODULES = YES; 725 | CLANG_ENABLE_OBJC_WEAK = YES; 726 | CODE_SIGN_IDENTITY = "iPhone Developer"; 727 | INFOPLIST_FILE = Tests/Info.plist; 728 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 729 | PRODUCT_BUNDLE_IDENTIFIER = "com.bojandimovski.ThingyTests-iOS"; 730 | PRODUCT_NAME = "$(TARGET_NAME)"; 731 | SDKROOT = iphoneos; 732 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 733 | SWIFT_VERSION = 5.0; 734 | }; 735 | name = Debug; 736 | }; 737 | /* End XCBuildConfiguration section */ 738 | 739 | /* Begin XCConfigurationList section */ 740 | 32D5B92D1DE3534C00C6ED8C /* Build configuration list for PBXNativeTarget "Thingy watchOS" */ = { 741 | isa = XCConfigurationList; 742 | buildConfigurations = ( 743 | 32D5B92E1DE3534C00C6ED8C /* Debug */, 744 | 32D5B92F1DE3534C00C6ED8C /* Release */, 745 | ); 746 | defaultConfigurationIsVisible = 0; 747 | defaultConfigurationName = Release; 748 | }; 749 | 32D5B93A1DE3536900C6ED8C /* Build configuration list for PBXNativeTarget "Thingy tvOS" */ = { 750 | isa = XCConfigurationList; 751 | buildConfigurations = ( 752 | 32D5B93B1DE3536900C6ED8C /* Debug */, 753 | 32D5B93C1DE3536900C6ED8C /* Release */, 754 | ); 755 | defaultConfigurationIsVisible = 0; 756 | defaultConfigurationName = Release; 757 | }; 758 | AD88C5FB5FC75605B8C40FDA /* Build configuration list for PBXNativeTarget "Tests" */ = { 759 | isa = XCConfigurationList; 760 | buildConfigurations = ( 761 | AD88CE3C2205A05E57F5E50A /* Debug */, 762 | AD88C352FCCA2C20DD77D4A9 /* Release */, 763 | ); 764 | defaultConfigurationIsVisible = 0; 765 | defaultConfigurationName = Release; 766 | }; 767 | AD88C620B3BDE5FD2AEDC8A8 /* Build configuration list for PBXNativeTarget "Thingy iOS" */ = { 768 | isa = XCConfigurationList; 769 | buildConfigurations = ( 770 | AD88C01082EF1EE75F31C9E6 /* Debug */, 771 | AD88CA0DED769975DBE9DD62 /* Release */, 772 | ); 773 | defaultConfigurationIsVisible = 0; 774 | defaultConfigurationName = Release; 775 | }; 776 | AD88CB75C26FB3DF1A51E108 /* Build configuration list for PBXProject "Thingy" */ = { 777 | isa = XCConfigurationList; 778 | buildConfigurations = ( 779 | AD88C55B31FDA3A3AE9EEFFE /* Debug */, 780 | AD88C147F31784D254F85D98 /* Release */, 781 | ); 782 | defaultConfigurationIsVisible = 0; 783 | defaultConfigurationName = Release; 784 | }; 785 | /* End XCConfigurationList section */ 786 | }; 787 | rootObject = AD88CDF9FD3BBF5B2C09AF5D /* Project object */; 788 | } 789 | -------------------------------------------------------------------------------- /Thingy.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Thingy.xcodeproj/xcshareddata/xcschemes/Tests.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 31 | 32 | 34 | 40 | 41 | 42 | 43 | 44 | 54 | 55 | 61 | 62 | 63 | 64 | 70 | 71 | 73 | 74 | 77 | 78 | 79 | -------------------------------------------------------------------------------- /Thingy.xcodeproj/xcshareddata/xcschemes/Thingy iOS.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 31 | 32 | 38 | 39 | 40 | 41 | 43 | 49 | 50 | 51 | 52 | 53 | 63 | 64 | 70 | 71 | 72 | 73 | 79 | 80 | 86 | 87 | 88 | 89 | 91 | 92 | 95 | 96 | 97 | -------------------------------------------------------------------------------- /Thingy.xcodeproj/xcshareddata/xcschemes/Thingy tvOS.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 31 | 32 | 34 | 40 | 41 | 42 | 43 | 44 | 50 | 51 | 52 | 53 | 54 | 55 | 65 | 66 | 72 | 73 | 74 | 75 | 76 | 77 | 83 | 84 | 90 | 91 | 92 | 93 | 95 | 96 | 99 | 100 | 101 | -------------------------------------------------------------------------------- /Thingy.xcodeproj/xcshareddata/xcschemes/Thingy watchOS.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 31 | 32 | 33 | 34 | 35 | 36 | 46 | 47 | 53 | 54 | 55 | 56 | 57 | 58 | 64 | 65 | 71 | 72 | 73 | 74 | 76 | 77 | 80 | 81 | 82 | -------------------------------------------------------------------------------- /codecov.yml: -------------------------------------------------------------------------------- 1 | ignore: 2 | - "Tests/**/*" 3 | -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | # Reference Documentation 2 | 3 | ## Protocols 4 | 5 | - [MarketingProtocol](protocols/MarketingProtocol.md) 6 | 7 | ## Structs 8 | 9 | - [Display](structs/Display.md) 10 | 11 | ## Enums 12 | 13 | - [Device](enums/Device.md) 14 | - [Display.ColorSpace](enums/Display.ColorSpace.md) 15 | - [Display.Size](enums/Display.Size.md) 16 | - [Family](enums/Family.md) 17 | - [Lines](enums/Lines.md) 18 | - [Lines.iPad](enums/Lines.iPad.md) 19 | - [Lines.iPhone](enums/Lines.iPhone.md) 20 | - [ThingyError](enums/ThingyError.md) 21 | 22 | ## Extensions 23 | 24 | - [Device](extensions/Device.md) 25 | - [Display.Size](extensions/Display.Size.md) 26 | - [Family](extensions/Family.md) 27 | - [RawDevice](extensions/RawDevice.md) 28 | 29 | ## Typealiases 30 | 31 | - [ProductLine](typealiases/ProductLine.md) 32 | 33 | This file was generated by [SourceDocs](https://github.com/eneko/SourceDocs) on 2020-07-20 10:59:24 +0000 -------------------------------------------------------------------------------- /docs/enums/ColorSpace.md: -------------------------------------------------------------------------------- 1 | **ENUM** 2 | 3 | # `ColorSpace` 4 | 5 | ```swift 6 | public enum ColorSpace 7 | ``` 8 | 9 | > An enum that describes all color spaces. 10 | 11 | ## Cases 12 | ### `p3` 13 | 14 | ```swift 15 | case p3 16 | ``` 17 | 18 | > - Wide color display (P3) 19 | 20 | ### `sRGB` 21 | 22 | ```swift 23 | case sRGB 24 | ``` 25 | 26 | > - Full sRGB standard 27 | -------------------------------------------------------------------------------- /docs/enums/Device.md: -------------------------------------------------------------------------------- 1 | **ENUM** 2 | 3 | # `Device` 4 | 5 | ```swift 6 | public enum Device: CaseIterable 7 | ``` 8 | 9 | A type that describes the specific device model. 10 | 11 | For iOS devices, only devices supporting iOS 8 are included. 12 | 13 | ## Cases 14 | ### `iPhone4S` 15 | 16 | ```swift 17 | case iPhone4S 18 | ``` 19 | 20 | - iPhone4S: iPhone 4S. 21 | 22 | ### `iPhone5` 23 | 24 | ```swift 25 | case iPhone5 26 | ``` 27 | 28 | - iPhone5: All iPhone 5 variants. 29 | 30 | ### `iPhone5s` 31 | 32 | ```swift 33 | case iPhone5s 34 | ``` 35 | 36 | - iPhone5s: All iPhone 5s variants. 37 | 38 | ### `iPhone5c` 39 | 40 | ```swift 41 | case iPhone5c 42 | ``` 43 | 44 | - iPhone5c: All iPhone 5c variants. 45 | 46 | ### `iPhone6` 47 | 48 | ```swift 49 | case iPhone6 50 | ``` 51 | 52 | - iPhone6: All iPhone 6 variants. 53 | 54 | ### `iPhone6Plus` 55 | 56 | ```swift 57 | case iPhone6Plus 58 | ``` 59 | 60 | - iPhone6Plus: All iPhone 6 Plus variants. 61 | 62 | ### `iPhone6s` 63 | 64 | ```swift 65 | case iPhone6s 66 | ``` 67 | 68 | - iPhone6s: All iPhone 6s variants. 69 | 70 | ### `iPhone6sPlus` 71 | 72 | ```swift 73 | case iPhone6sPlus 74 | ``` 75 | 76 | - iPhone6sPlus: All iPhone 6s Plus variants. 77 | 78 | ### `iPhoneSE` 79 | 80 | ```swift 81 | case iPhoneSE 82 | ``` 83 | 84 | - iPhoneSE: All iPhone SE variants. 85 | 86 | ### `iPhoneSE2G` 87 | 88 | ```swift 89 | case iPhoneSE2G 90 | ``` 91 | 92 | - iPhoneSE2: All iPhone SE (2020) variants. 93 | 94 | ### `iPhone7` 95 | 96 | ```swift 97 | case iPhone7 98 | ``` 99 | 100 | - iPhone7: All iPhone 7 variants. 101 | 102 | ### `iPhone7Plus` 103 | 104 | ```swift 105 | case iPhone7Plus 106 | ``` 107 | 108 | - iPhone7Plus: All iPhone 7 Plus variants. 109 | 110 | ### `iPhone8` 111 | 112 | ```swift 113 | case iPhone8 114 | ``` 115 | 116 | - iPhone8: All iPhone 8 variants. 117 | 118 | ### `iPhone8Plus` 119 | 120 | ```swift 121 | case iPhone8Plus 122 | ``` 123 | 124 | - iPhone8Plus: All iPhone 8 Plus variants. 125 | 126 | ### `iPhoneX` 127 | 128 | ```swift 129 | case iPhoneX 130 | ``` 131 | 132 | - iPhoneX: All iPhone X variants. 133 | 134 | ### `iPhoneXR` 135 | 136 | ```swift 137 | case iPhoneXR 138 | ``` 139 | 140 | - iPhoneXR: All iPhone XR variants. 141 | 142 | ### `iPhoneXS` 143 | 144 | ```swift 145 | case iPhoneXS 146 | ``` 147 | 148 | - iPhoneXS: All iPhone XS variants. 149 | 150 | ### `iPhoneXSMax` 151 | 152 | ```swift 153 | case iPhoneXSMax 154 | ``` 155 | 156 | - iPhoneXSMax: All iPhone XS Max variants. 157 | 158 | ### `iPhone11` 159 | 160 | ```swift 161 | case iPhone11 162 | ``` 163 | 164 | - iPhone11: All iPhone 11 variants. 165 | 166 | ### `iPhone11Pro` 167 | 168 | ```swift 169 | case iPhone11Pro 170 | ``` 171 | 172 | - iPhone11Pro: All iPhone 11 Pro variants. 173 | 174 | ### `iPhone11ProMax` 175 | 176 | ```swift 177 | case iPhone11ProMax 178 | ``` 179 | 180 | - iPhone11ProMax: All iPhone 11 Pro Max variants. 181 | 182 | ### `iPad2` 183 | 184 | ```swift 185 | case iPad2 186 | ``` 187 | 188 | - iPad2: All iPad 2 variants. 189 | 190 | ### `iPad3` 191 | 192 | ```swift 193 | case iPad3 194 | ``` 195 | 196 | - iPad3: All iPad 3 variants. 197 | 198 | ### `iPad4` 199 | 200 | ```swift 201 | case iPad4 202 | ``` 203 | 204 | - iPad4: All iPad 4 variants. 205 | 206 | ### `iPad5` 207 | 208 | ```swift 209 | case iPad5 210 | ``` 211 | 212 | - iPad5: All iPad (2017) variants. 213 | 214 | ### `iPad6` 215 | 216 | ```swift 217 | case iPad6 218 | ``` 219 | 220 | - iPad6: All iPad (2018) variants. 221 | 222 | ### `iPad7` 223 | 224 | ```swift 225 | case iPad7 226 | ``` 227 | 228 | - iPad7: All iPad (2019) variants. 229 | 230 | ### `iPadMini` 231 | 232 | ```swift 233 | case iPadMini 234 | ``` 235 | 236 | - iPadMini: The original iPad mini in all variants. 237 | 238 | ### `iPadMini2` 239 | 240 | ```swift 241 | case iPadMini2 242 | ``` 243 | 244 | - iPadMini2: All iPad mini 2 variants. 245 | 246 | ### `iPadMini3` 247 | 248 | ```swift 249 | case iPadMini3 250 | ``` 251 | 252 | - iPadMini3: All iPad mini 3 variants. 253 | 254 | ### `iPadMini4` 255 | 256 | ```swift 257 | case iPadMini4 258 | ``` 259 | 260 | - iPadMini4: All iPad mini 4 variants. 261 | 262 | ### `iPadMini5` 263 | 264 | ```swift 265 | case iPadMini5 266 | ``` 267 | 268 | - iPadMini5: All iPad mini 5 variants. 269 | 270 | ### `iPadAir` 271 | 272 | ```swift 273 | case iPadAir 274 | ``` 275 | 276 | - iPadAir: The original iPad Air in all variants. 277 | 278 | ### `iPadAir2` 279 | 280 | ```swift 281 | case iPadAir2 282 | ``` 283 | 284 | - iPadAir2: All iPad Air 2 variants. 285 | 286 | ### `iPadAir3` 287 | 288 | ```swift 289 | case iPadAir3 290 | ``` 291 | 292 | - iPadAir3: All iPad Air 3 variants. 293 | 294 | ### `iPadPro12Inch` 295 | 296 | ```swift 297 | case iPadPro12Inch 298 | ``` 299 | 300 | - iPadPro12Inch: The 1st generation 12.9 inch iPad Pro in all variants. 301 | 302 | ### `iPadPro9Inch` 303 | 304 | ```swift 305 | case iPadPro9Inch 306 | ``` 307 | 308 | - iPadPro9Inch: The 9.7 inch iPad Pro in all variants. 309 | 310 | ### `iPadPro12Inch2G` 311 | 312 | ```swift 313 | case iPadPro12Inch2G 314 | ``` 315 | 316 | - iPadPro12Inch2G: The 2nd generation 12 inch iPad Pro in all variants. 317 | 318 | ### `iPadPro10Inch` 319 | 320 | ```swift 321 | case iPadPro10Inch 322 | ``` 323 | 324 | - iPadPro10Inch: The 10.5 inch iPad Pro in all variants. 325 | 326 | ### `iPadPro12Inch3G` 327 | 328 | ```swift 329 | case iPadPro12Inch3G 330 | ``` 331 | 332 | - iPadPro12Inch3G: The 3rd generation 12.9 inch iPad Pro in all variants. 333 | 334 | ### `iPadPro11Inch` 335 | 336 | ```swift 337 | case iPadPro11Inch 338 | ``` 339 | 340 | - iPadPro11Inch: The 11 inch iPad Pro in all variants. 341 | 342 | ### `iPodTouch5G` 343 | 344 | ```swift 345 | case iPodTouch5G 346 | ``` 347 | 348 | - iPodTouch5G: All iPod touch 5G variants. 349 | 350 | ### `iPodTouch6G` 351 | 352 | ```swift 353 | case iPodTouch6G 354 | ``` 355 | 356 | - iPodTouch6G: All iPod touch 6G variants. 357 | 358 | ### `iPodTouch7G` 359 | 360 | ```swift 361 | case iPodTouch7G 362 | ``` 363 | 364 | - iPodTouch7G: All iPod touch 7G variants. 365 | 366 | ### `appleTV4` 367 | 368 | ```swift 369 | case appleTV4 370 | ``` 371 | 372 | - appleTV4: Apple TV 4. 373 | 374 | ### `appleTV4K` 375 | 376 | ```swift 377 | case appleTV4K 378 | ``` 379 | 380 | - appleTV4K: Apple TV 4K. 381 | 382 | ### `watch(_:)` 383 | 384 | ```swift 385 | case watch(Display.Size) 386 | ``` 387 | 388 | - watch: The original Apple Watch. 389 | 390 | ### `watchSeries1(_:)` 391 | 392 | ```swift 393 | case watchSeries1(Display.Size) 394 | ``` 395 | 396 | - watchSeries1: Apple Watch Series 1. 397 | 398 | ### `watchSeries2(_:)` 399 | 400 | ```swift 401 | case watchSeries2(Display.Size) 402 | ``` 403 | 404 | - watchSeries2: Apple Watch Series 2. 405 | 406 | ### `watchSeries3(_:)` 407 | 408 | ```swift 409 | case watchSeries3(Display.Size) 410 | ``` 411 | 412 | - watchSeries3: Apple Watch Series 3. 413 | 414 | ### `watchSeries4(_:)` 415 | 416 | ```swift 417 | case watchSeries4(Display.Size) 418 | ``` 419 | 420 | - watchSeries4: Apple Watch Series 4. 421 | 422 | ### `watchSeries5(_:)` 423 | 424 | ```swift 425 | case watchSeries5(Display.Size) 426 | ``` 427 | 428 | - watchSeries5: Apple Watch Series 5. 429 | 430 | ### `simulator(_:)` 431 | 432 | ```swift 433 | indirect case simulator(Device) 434 | ``` 435 | 436 | - simulator: A simulator for the associated device model. 437 | 438 | ### `unknown(_:)` 439 | 440 | ```swift 441 | case unknown(Family) 442 | ``` 443 | 444 | - unknown: An unknown or a future device within the associated family. 445 | -------------------------------------------------------------------------------- /docs/enums/Display.ColorSpace.md: -------------------------------------------------------------------------------- 1 | **ENUM** 2 | 3 | # `Display.ColorSpace` 4 | 5 | ```swift 6 | public enum ColorSpace 7 | ``` 8 | 9 | An enum that describes all color spaces. 10 | 11 | ## Cases 12 | ### `p3` 13 | 14 | ```swift 15 | case p3 16 | ``` 17 | 18 | - Wide color display (P3) 19 | 20 | ### `sRGB` 21 | 22 | ```swift 23 | case sRGB 24 | ``` 25 | 26 | - Full sRGB standard 27 | -------------------------------------------------------------------------------- /docs/enums/Display.Size.md: -------------------------------------------------------------------------------- 1 | **ENUM** 2 | 3 | # `Display.Size` 4 | 5 | ```swift 6 | public enum Size: Float 7 | ``` 8 | 9 | An enum that describes the display size. 10 | 11 | ## Cases 12 | ### `screen38mm` 13 | 14 | ```swift 15 | case screen38mm = 38 16 | ``` 17 | 18 | - screen38mm: 38mm Apple Watch screen. 19 | 20 | ### `screen42mm` 21 | 22 | ```swift 23 | case screen42mm = 42 24 | ``` 25 | 26 | - screen42mm: 42mm Apple Watch screen. 27 | 28 | ### `screen40mm` 29 | 30 | ```swift 31 | case screen40mm = 40 32 | ``` 33 | 34 | - screen40mm: 40mm Apple Watch screen. 35 | 36 | ### `screen44mm` 37 | 38 | ```swift 39 | case screen44mm = 44 40 | ``` 41 | 42 | - screen44mm: 44mm Apple Watch screen. 43 | 44 | ### `screen3_5Inch` 45 | 46 | ```swift 47 | case screen3_5Inch = 3.5 48 | ``` 49 | 50 | - screen3_5Inch: 3.5in iPhone screen. 51 | 52 | ### `screen4Inch` 53 | 54 | ```swift 55 | case screen4Inch = 4 56 | ``` 57 | 58 | - screen4Inch: 4in iPhone/iPod touch screen. 59 | 60 | ### `screen4_7Inch` 61 | 62 | ```swift 63 | case screen4_7Inch = 4.7 64 | ``` 65 | 66 | - screen4_7Inch: 4.7in iPhone screen. 67 | 68 | ### `screen5_5Inch` 69 | 70 | ```swift 71 | case screen5_5Inch = 5.5 72 | ``` 73 | 74 | - screen5_5Inch: 5.5in iPhone screen. 75 | 76 | ### `screen5_8Inch` 77 | 78 | ```swift 79 | case screen5_8Inch = 5.8 80 | ``` 81 | 82 | - screen5_8Inch: 5.8in iPhone screen. 83 | 84 | ### `screen6_1Inch` 85 | 86 | ```swift 87 | case screen6_1Inch = 6.1 88 | ``` 89 | 90 | - screen6_1Inch: 6.1in iPhone screen. 91 | 92 | ### `screen6_5Inch` 93 | 94 | ```swift 95 | case screen6_5Inch = 6.5 96 | ``` 97 | 98 | - screen6_5Inch: 6.5in iPhone screen. 99 | 100 | ### `screen7_9Inch` 101 | 102 | ```swift 103 | case screen7_9Inch = 7.9 104 | ``` 105 | 106 | - screen7_9Inch: 7.9in iPad screen. 107 | 108 | ### `screen9_7Inch` 109 | 110 | ```swift 111 | case screen9_7Inch = 9.7 112 | ``` 113 | 114 | - screen9_7Inch: 9.7in iPad screen. 115 | 116 | ### `screen10_2Inch` 117 | 118 | ```swift 119 | case screen10_2Inch = 10.2 120 | ``` 121 | 122 | - screen10_5Inch: 10.2in iPad screen. 123 | 124 | ### `screen10_5Inch` 125 | 126 | ```swift 127 | case screen10_5Inch = 10.5 128 | ``` 129 | 130 | - screen10_5Inch: 10.5in iPad screen. 131 | 132 | ### `screen11Inch` 133 | 134 | ```swift 135 | case screen11Inch = 11 136 | ``` 137 | 138 | - screen10_5Inch: 10.5in iPad screen. 139 | 140 | ### `screen12_9Inch` 141 | 142 | ```swift 143 | case screen12_9Inch = 12.9 144 | ``` 145 | 146 | - screen12_9Inch: 12.9in iPad screen. 147 | 148 | ### `notApplicable` 149 | 150 | ```swift 151 | case notApplicable = -1 152 | ``` 153 | 154 | - notApplicable: Not applicable, in case of the Apple TV. 155 | -------------------------------------------------------------------------------- /docs/enums/Family.md: -------------------------------------------------------------------------------- 1 | **ENUM** 2 | 3 | # `Family` 4 | 5 | ```swift 6 | public enum Family: String 7 | ``` 8 | 9 | A type that describes the product family. 10 | 11 | ## Cases 12 | ### `phone` 13 | 14 | ```swift 15 | case phone = "iPhone" 16 | ``` 17 | 18 | - phone: iPhone product family. 19 | 20 | ### `pod` 21 | 22 | ```swift 23 | case pod = "iPod" 24 | ``` 25 | 26 | - pod: iPod touch product family. 27 | 28 | ### `pad` 29 | 30 | ```swift 31 | case pad = "iPad" 32 | ``` 33 | 34 | - pad: iPad product family. 35 | 36 | ### `tv` 37 | 38 | ```swift 39 | case tv = "AppleTV" 40 | ``` 41 | 42 | - tv: Apple TV product family. 43 | 44 | ### `watch` 45 | 46 | ```swift 47 | case watch = "Watch" 48 | ``` 49 | 50 | - watch: Apple Watch product family. 51 | -------------------------------------------------------------------------------- /docs/enums/Lines.iPad.md: -------------------------------------------------------------------------------- 1 | **ENUM** 2 | 3 | # `Lines.iPad` 4 | 5 | ```swift 6 | public enum iPad: ProductLine 7 | ``` 8 | 9 | A type that describes the specific iPad line. 10 | 11 | ## Cases 12 | ### `standard` 13 | 14 | ```swift 15 | case standard 16 | ``` 17 | 18 | - standard: A standard iPad product. 19 | 20 | ### `pro` 21 | 22 | ```swift 23 | case pro 24 | ``` 25 | 26 | - pro: An iPad Pro product. 27 | 28 | ### `mini` 29 | 30 | ```swift 31 | case mini 32 | ``` 33 | 34 | - mini: An iPad mini product. 35 | 36 | ### `air` 37 | 38 | ```swift 39 | case air 40 | ``` 41 | 42 | - air: An iPad Air product. 43 | 44 | ## Properties 45 | ### `marketingName` 46 | 47 | ```swift 48 | public var marketingName: String 49 | ``` 50 | 51 | The marketing name of the iPad product line, e.g. "Pro", "Air". 52 | -------------------------------------------------------------------------------- /docs/enums/Lines.iPhone.md: -------------------------------------------------------------------------------- 1 | **ENUM** 2 | 3 | # `Lines.iPhone` 4 | 5 | ```swift 6 | public enum iPhone: ProductLine 7 | ``` 8 | 9 | A type that describes the specific iPhone line. 10 | 11 | ## Cases 12 | ### `standard` 13 | 14 | ```swift 15 | case standard 16 | ``` 17 | 18 | - standard: A standard iPhone product. 19 | 20 | ### `pro` 21 | 22 | ```swift 23 | case pro 24 | ``` 25 | 26 | - pro: An iPhone Pro product. 27 | 28 | ### `se` 29 | 30 | ```swift 31 | case se 32 | ``` 33 | 34 | - se: An iPhone SE product. 35 | 36 | ## Properties 37 | ### `marketingName` 38 | 39 | ```swift 40 | public var marketingName: String 41 | ``` 42 | 43 | The marketing name of the iPhone product line, e.g. "Pro", "SE". 44 | -------------------------------------------------------------------------------- /docs/enums/Lines.md: -------------------------------------------------------------------------------- 1 | **ENUM** 2 | 3 | # `Lines` 4 | 5 | ```swift 6 | public enum Lines 7 | ``` 8 | 9 | A struct containing all product lines. Currently iPad only. 10 | -------------------------------------------------------------------------------- /docs/enums/Size.md: -------------------------------------------------------------------------------- 1 | **ENUM** 2 | 3 | # `Size` 4 | 5 | ```swift 6 | public enum Size: Float 7 | ``` 8 | 9 | > An enum that describes the display size. 10 | 11 | ## Cases 12 | ### `screen38mm` 13 | 14 | ```swift 15 | case screen38mm = 38 16 | ``` 17 | 18 | > - screen38mm: 38mm Apple Watch screen. 19 | 20 | ### `screen42mm` 21 | 22 | ```swift 23 | case screen42mm = 42 24 | ``` 25 | 26 | > - screen42mm: 42mm Apple Watch screen. 27 | 28 | ### `screen40mm` 29 | 30 | ```swift 31 | case screen40mm = 40 32 | ``` 33 | 34 | > - screen40mm: 40mm Apple Watch screen. 35 | 36 | ### `screen44mm` 37 | 38 | ```swift 39 | case screen44mm = 44 40 | ``` 41 | 42 | > - screen44mm: 44mm Apple Watch screen. 43 | 44 | ### `screen3_5Inch` 45 | 46 | ```swift 47 | case screen3_5Inch = 3.5 48 | ``` 49 | 50 | > - screen3_5Inch: 3.5in iPhone screen. 51 | 52 | ### `screen4Inch` 53 | 54 | ```swift 55 | case screen4Inch = 4 56 | ``` 57 | 58 | > - screen4Inch: 4in iPhone/iPod touch screen. 59 | 60 | ### `screen4_7Inch` 61 | 62 | ```swift 63 | case screen4_7Inch = 4.7 64 | ``` 65 | 66 | > - screen4_7Inch: 4.7in iPhone screen. 67 | 68 | ### `screen5_5Inch` 69 | 70 | ```swift 71 | case screen5_5Inch = 5.5 72 | ``` 73 | 74 | > - screen5_5Inch: 5.5in iPhone screen. 75 | 76 | ### `screen5_8Inch` 77 | 78 | ```swift 79 | case screen5_8Inch = 5.8 80 | ``` 81 | 82 | > - screen5_8Inch: 5.8in iPhone screen. 83 | 84 | ### `screen6_1Inch` 85 | 86 | ```swift 87 | case screen6_1Inch = 6.1 88 | ``` 89 | 90 | > - screen6_1Inch: 6.1in iPhone screen. 91 | 92 | ### `screen6_5Inch` 93 | 94 | ```swift 95 | case screen6_5Inch = 6.5 96 | ``` 97 | 98 | > - screen6_5Inch: 6.5in iPhone screen. 99 | 100 | ### `screen7_9Inch` 101 | 102 | ```swift 103 | case screen7_9Inch = 7.9 104 | ``` 105 | 106 | > - screen7_9Inch: 7.9in iPad screen. 107 | 108 | ### `screen9_7Inch` 109 | 110 | ```swift 111 | case screen9_7Inch = 9.7 112 | ``` 113 | 114 | > - screen9_7Inch: 9.7in iPad screen. 115 | 116 | ### `screen10_2Inch` 117 | 118 | ```swift 119 | case screen10_2Inch = 10.2 120 | ``` 121 | 122 | > - screen10_5Inch: 10.2in iPad screen. 123 | 124 | ### `screen10_5Inch` 125 | 126 | ```swift 127 | case screen10_5Inch = 10.5 128 | ``` 129 | 130 | > - screen10_5Inch: 10.5in iPad screen. 131 | 132 | ### `screen11Inch` 133 | 134 | ```swift 135 | case screen11Inch = 11 136 | ``` 137 | 138 | > - screen10_5Inch: 10.5in iPad screen. 139 | 140 | ### `screen12_9Inch` 141 | 142 | ```swift 143 | case screen12_9Inch = 12.9 144 | ``` 145 | 146 | > - screen12_9Inch: 12.9in iPad screen. 147 | 148 | ### `notApplicable` 149 | 150 | ```swift 151 | case notApplicable = -1 152 | ``` 153 | 154 | > - notApplicable: Not applicable, in case of the Apple TV. 155 | -------------------------------------------------------------------------------- /docs/enums/ThingyError.md: -------------------------------------------------------------------------------- 1 | **ENUM** 2 | 3 | # `ThingyError` 4 | 5 | ```swift 6 | public enum ThingyError: Error 7 | ``` 8 | 9 | Errors thrown by Thingy. 10 | 11 | ## Cases 12 | ### `incomparableFamilies` 13 | 14 | ```swift 15 | case incomparableFamilies 16 | ``` 17 | 18 | - An error thrown when devices of different families get compared. 19 | 20 | ### `incomparableProductLines` 21 | 22 | ```swift 23 | case incomparableProductLines 24 | ``` 25 | 26 | - An error thrown when devices from different product lines get compared. 27 | 28 | ### `incomparableUnknownProduct` 29 | 30 | ```swift 31 | case incomparableUnknownProduct 32 | ``` 33 | 34 | - An error thrown when an unknown device gets compared. 35 | -------------------------------------------------------------------------------- /docs/enums/iPad.md: -------------------------------------------------------------------------------- 1 | **ENUM** 2 | 3 | # `iPad` 4 | 5 | ```swift 6 | public enum iPad: ProductLine 7 | ``` 8 | 9 | > A type that describes the specific iPad line. 10 | 11 | ## Cases 12 | ### `regular` 13 | 14 | ```swift 15 | case regular 16 | ``` 17 | 18 | > - regular: A standard iPad product. 19 | 20 | ### `pro` 21 | 22 | ```swift 23 | case pro 24 | ``` 25 | 26 | > - pro: An iPad Pro product. 27 | 28 | ### `mini` 29 | 30 | ```swift 31 | case mini 32 | ``` 33 | 34 | > - mini: An iPad mini product. 35 | 36 | ### `air` 37 | 38 | ```swift 39 | case air 40 | ``` 41 | 42 | > - air: An iPad Air product. 43 | 44 | ## Properties 45 | ### `marketingName` 46 | 47 | ```swift 48 | public var marketingName: String 49 | ``` 50 | 51 | > The marketing name of the iPad product line, e.g. "Pro", "Air". 52 | -------------------------------------------------------------------------------- /docs/extensions/Device.md: -------------------------------------------------------------------------------- 1 | **EXTENSION** 2 | 3 | # `Device` 4 | ```swift 5 | public extension Device 6 | ``` 7 | 8 | ## Properties 9 | ### `displaySize` 10 | 11 | ```swift 12 | var displaySize: Display.Size 13 | ``` 14 | 15 | Returns the display size in inches. 16 | 17 | ### `display` 18 | 19 | ```swift 20 | var display: Display 21 | ``` 22 | 23 | Returns an extended info on the display, including all resolutions, scale and density. 24 | 25 | ### `family` 26 | 27 | ```swift 28 | var family: Family 29 | ``` 30 | 31 | Associated family for each device. 32 | 33 | ### `marketingName` 34 | 35 | ```swift 36 | public var marketingName: String 37 | ``` 38 | 39 | The full marketing name of the model, e.g. "iPhone 7 Plus", "Apple TV 4". 40 | 41 | ### `productLine` 42 | 43 | ```swift 44 | var productLine: ProductLine? 45 | ``` 46 | 47 | Product line of the model. 48 | 49 | ## Methods 50 | ### `init(identifier:)` 51 | 52 | ```swift 53 | init?(identifier: String? = nil) 54 | ``` 55 | 56 | Creates a custom device out of a valid identifier. 57 | 58 | If a device cannot be created, returns an unknown device. 59 | 60 | - Parameter identifier: A device identifier, e.g. "iPhone9,2", "iPad6,11.", "AppleTV5,3". 61 | 62 | #### Parameters 63 | 64 | | Name | Description | 65 | | ---- | ----------- | 66 | | identifier | A device identifier, e.g. “iPhone9,2”, “iPad6,11.”, “AppleTV5,3”. | 67 | 68 | ### `isEqual(to:)` 69 | 70 | ```swift 71 | func isEqual(to compared: Device) throws -> Bool 72 | ``` 73 | 74 | Checks if the current device is the same as the compared model. 75 | 76 | - Parameter to: A model to compare the current device against. 77 | - Returns: True if the device is the same, and false otherwise. 78 | - Throws: An error when comparing incompatible families, product lines or unknown products. 79 | 80 | #### Parameters 81 | 82 | | Name | Description | 83 | | ---- | ----------- | 84 | | to | A model to compare the current device against. | 85 | 86 | ### `isNewerOrEqual(than:)` 87 | 88 | ```swift 89 | func isNewerOrEqual(than compared: Device) throws -> Bool 90 | ``` 91 | 92 | Checks if the current device is newer (or same) than the compared model. 93 | 94 | - Parameter than: A model to compare the current device against. 95 | - Returns: True if the device is newer or the same, and false if it's older. 96 | - Throws: An error when comparing incompatible families, product lines or unknown products. 97 | 98 | #### Parameters 99 | 100 | | Name | Description | 101 | | ---- | ----------- | 102 | | than | A model to compare the current device against. | 103 | 104 | ### `isOlder(than:)` 105 | 106 | ```swift 107 | func isOlder(than compared: Device) throws -> Bool 108 | ``` 109 | 110 | Checks if the current device is older than the compared model. 111 | 112 | - Parameter than: A model to compare the current device against. 113 | - Returns: True if the device is older, and false if it's newer or the same. 114 | - Throws: An error when comparing incompatible families, product lines or unknown products. 115 | 116 | #### Parameters 117 | 118 | | Name | Description | 119 | | ---- | ----------- | 120 | | than | A model to compare the current device against. | -------------------------------------------------------------------------------- /docs/extensions/Display.Size.md: -------------------------------------------------------------------------------- 1 | **EXTENSION** 2 | 3 | # `Display.Size` 4 | ```swift 5 | extension Display.Size: Comparable 6 | ``` 7 | 8 | ## Methods 9 | ### `<(_:_:)` 10 | 11 | ```swift 12 | public static func <(lhs: Display.Size, rhs: Display.Size) -> Bool 13 | ``` 14 | 15 | #### Parameters 16 | 17 | | Name | Description | 18 | | ---- | ----------- | 19 | | lhs | A value to compare. | 20 | | rhs | Another value to compare. | 21 | 22 | ### `==(_:_:)` 23 | 24 | ```swift 25 | public static func ==(lhs: Display.Size, rhs: Display.Size) -> Bool 26 | ``` 27 | 28 | #### Parameters 29 | 30 | | Name | Description | 31 | | ---- | ----------- | 32 | | lhs | A value to compare. | 33 | | rhs | Another value to compare. | -------------------------------------------------------------------------------- /docs/extensions/Family.md: -------------------------------------------------------------------------------- 1 | **EXTENSION** 2 | 3 | # `Family` 4 | ```swift 5 | extension Family: MarketingProtocol 6 | ``` 7 | 8 | ## Properties 9 | ### `marketingName` 10 | 11 | ```swift 12 | public var marketingName: String 13 | ``` 14 | 15 | The marketing name of the product family, e.g. "iPhone", "Apple TV". 16 | -------------------------------------------------------------------------------- /docs/extensions/RawDevice.md: -------------------------------------------------------------------------------- 1 | **EXTENSION** 2 | 3 | # `RawDevice` 4 | ```swift 5 | extension RawDevice: Comparable 6 | ``` 7 | 8 | ## Methods 9 | ### `<(_:_:)` 10 | 11 | ```swift 12 | public static func <(lhs: RawDevice, rhs: RawDevice) -> Bool 13 | ``` 14 | 15 | #### Parameters 16 | 17 | | Name | Description | 18 | | ---- | ----------- | 19 | | lhs | A value to compare. | 20 | | rhs | Another value to compare. | 21 | 22 | ### `==(_:_:)` 23 | 24 | ```swift 25 | public static func ==(lhs: RawDevice, rhs: RawDevice) -> Bool 26 | ``` 27 | 28 | #### Parameters 29 | 30 | | Name | Description | 31 | | ---- | ----------- | 32 | | lhs | A value to compare. | 33 | | rhs | Another value to compare. | -------------------------------------------------------------------------------- /docs/protocols/MarketingProtocol.md: -------------------------------------------------------------------------------- 1 | **PROTOCOL** 2 | 3 | # `MarketingProtocol` 4 | 5 | ```swift 6 | public protocol MarketingProtocol 7 | ``` 8 | 9 | :nodoc: 10 | A protocol that describes products (lines and models). 11 | 12 | ## Properties 13 | ### `marketingName` 14 | 15 | ```swift 16 | var marketingName: String 17 | ``` 18 | 19 | The full marketing name of the entity. 20 | -------------------------------------------------------------------------------- /docs/structs/Display.md: -------------------------------------------------------------------------------- 1 | **STRUCT** 2 | 3 | # `Display` 4 | 5 | ```swift 6 | public struct Display: Equatable 7 | ``` 8 | 9 | A struct that provides an extended info on the display. 10 | 11 | ## Properties 12 | ### `size` 13 | 14 | ```swift 15 | public var size: Size 16 | ``` 17 | 18 | Screen size in inches. 19 | 20 | ### `resolution` 21 | 22 | ```swift 23 | public var resolution: CGSize 24 | ``` 25 | 26 | Resolution of the device. 27 | 28 | ### `physicalResolution` 29 | 30 | ```swift 31 | public var physicalResolution: CGSize 32 | ``` 33 | 34 | Full physical resolution of the device, without any down-/up-sampling. 35 | 36 | ### `renderedResolution` 37 | 38 | ```swift 39 | public var renderedResolution: CGSize 40 | ``` 41 | 42 | Rendered resolution of the device, with down-/up-sampling. 43 | 44 | ### `scale` 45 | 46 | ```swift 47 | public var scale: Float 48 | ``` 49 | 50 | Screen scale, 1.0 for non-Retina devices. 51 | 52 | ### `density` 53 | 54 | ```swift 55 | public var density: Int 56 | ``` 57 | 58 | Density of the display in PPI (pixels-per-inch). 59 | 60 | ### `hasTrueTone` 61 | 62 | ```swift 63 | public var hasTrueTone: Bool 64 | ``` 65 | 66 | True Tone display. 67 | 68 | ### `colorSpace` 69 | 70 | ```swift 71 | public var colorSpace: ColorSpace 72 | ``` 73 | 74 | Color space. 75 | -------------------------------------------------------------------------------- /docs/structs/Lines.md: -------------------------------------------------------------------------------- 1 | **STRUCT** 2 | 3 | # `Lines` 4 | 5 | ```swift 6 | public struct Lines 7 | ``` 8 | 9 | > A struct containing all product lines. Currently iPad only. 10 | -------------------------------------------------------------------------------- /docs/typealiases/ProductLine.md: -------------------------------------------------------------------------------- 1 | **TYPEALIAS** 2 | 3 | # `ProductLine` 4 | 5 | ```swift 6 | public typealias ProductLine = MarketingProtocol 7 | ``` 8 | 9 | :nodoc: 10 | A dummy protocol used to encompass all product line enums. --------------------------------------------------------------------------------