├── .github ├── FUNDING.yml └── workflows │ ├── Check for New Devices.yml │ ├── Pull Request.yml │ └── Update Code Coverage.yml ├── .gitignore ├── Assets └── logo │ ├── logo.fig │ └── logo.svg ├── Generator ├── Package.swift ├── README.md ├── Sources │ └── Generator │ │ ├── Resources │ │ └── Models - The iPhone Wiki.html │ │ └── main.swift └── Tests │ └── GeneratorTests │ └── GeneratorTests.swift ├── LICENSE ├── Package.swift ├── README.md ├── Sources └── Devices │ ├── DeviceType.swift │ ├── Devices.swift │ ├── Extensions │ └── UIDevice+Extension.swift │ └── Types │ ├── Airpod.swift │ ├── Airtag.swift │ ├── ApplePencil.swift │ ├── AppleTV.swift │ ├── AppleWatch.swift │ ├── HomePod.swift │ ├── MacBookAir.swift │ ├── MacBookPro.swift │ ├── MacMini.swift │ ├── SiriRemote.swift │ ├── SmartKeyboard.swift │ ├── iMac.swift │ ├── iPad.swift │ ├── iPadAir.swift │ ├── iPadMini.swift │ ├── iPadPro.swift │ ├── iPhone.swift │ └── iPodTouch.swift └── Tests └── DevicesTests ├── Devices_Tests.swift └── Types ├── Airpod_Tests.swift ├── Airtag_Tests.swift ├── ApplePencil_Tests.swift ├── AppleTV_Tests.swift ├── AppleWatch_Tests.swift ├── HomePod_Tests.swift ├── MacBookAir_Tests.swift ├── MacBookPro_Tests.swift ├── MacMini_Tests.swift ├── SiriRemote_Tests.swift ├── SmartKeyboard_Tests.swift ├── iMac_Tests.swift ├── iPadAir_Tests.swift ├── iPadMini_Tests.swift ├── iPadPro_Tests.swift ├── iPad_Tests.swift ├── iPhone_Tests.swift └── iPodTouch_Tests.swift /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: [ptrkstr] 4 | patreon: # Replace with a single Patreon username 5 | open_collective: # Replace with a single Open Collective username 6 | ko_fi: ptrkstr 7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | liberapay: # Replace with a single Liberapay username 10 | issuehunt: # Replace with a single IssueHunt username 11 | otechie: # Replace with a single Otechie username 12 | custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] 13 | -------------------------------------------------------------------------------- /.github/workflows/Check for New Devices.yml: -------------------------------------------------------------------------------- 1 | name: Check for New Devices 2 | 3 | on: 4 | schedule: 5 | - cron: '0 3 * * *' 6 | 7 | concurrency: 8 | group: ${{ github.workflow }} 9 | cancel-in-progress: true 10 | 11 | jobs: 12 | job: 13 | runs-on: macos-latest 14 | 15 | steps: 16 | - uses: actions/checkout@v2 17 | - name: Run Generator 18 | run: cd Generator; swift run 19 | - name: Check for changes 20 | run: | 21 | # true if changes detected 22 | isChanged=$(git diff-index --quiet HEAD -- || echo "true"); 23 | echo "IS_CHANGED=$isChanged" >> $GITHUB_ENV 24 | if [[ $isChanged == "true" ]]; then 25 | echo "Changes detected" 26 | else 27 | echo "No changes detected" 28 | fi 29 | - name: "Ensure package still builds" 30 | if: ${{ env.IS_CHANGED == 'true' }} 31 | run: swift build --target Devices 32 | - name: "Create pull request" 33 | if: ${{ env.IS_CHANGED == 'true' }} 34 | uses: peter-evans/create-pull-request@v3 35 | with: 36 | commit-message: "Update devices (autogenerated)" 37 | branch: "automated/update-devices" 38 | delete-branch: true 39 | title: "Update devices (autogenerated)" 40 | body: "" 41 | assignees: "ptrkstr" 42 | -------------------------------------------------------------------------------- /.github/workflows/Pull Request.yml: -------------------------------------------------------------------------------- 1 | name: Pull Request 2 | 3 | on: 4 | pull_request: 5 | branches: [ develop ] 6 | 7 | concurrency: 8 | group: ${{ github.head_ref }} 9 | cancel-in-progress: true 10 | 11 | jobs: 12 | job: 13 | runs-on: macos-latest 14 | 15 | steps: 16 | - uses: actions/checkout@v2 17 | - name: Build 18 | run: swift build 19 | - name: Run tests 20 | run: swift test --enable-test-discovery --enable-code-coverage 21 | -------------------------------------------------------------------------------- /.github/workflows/Update Code Coverage.yml: -------------------------------------------------------------------------------- 1 | name: Update Code Coverage 2 | 3 | on: 4 | push: 5 | branches: [ develop ] 6 | 7 | concurrency: 8 | group: ${{ github.workflow }} 9 | cancel-in-progress: true 10 | 11 | jobs: 12 | job: 13 | runs-on: macos-latest 14 | 15 | steps: 16 | - uses: actions/checkout@v2 17 | - name: Build 18 | run: swift build 19 | - name: Run tests 20 | run: swift test --enable-test-discovery --enable-code-coverage 21 | # https://nacho4d-nacho4d.blogspot.com/2020/10/code-coverage-for-swift-package-projects.html 22 | - name: Convert code coverage 23 | run: xcrun llvm-cov export -format="lcov" -instr-profile=$(find .build -name default.profdata) $(find .build -name DevicesPackageTests) > info.lcov 24 | - name: Upload coverage to Codecov 25 | uses: codecov/codecov-action@v2 26 | with: 27 | token: ${{ secrets.CODECOV_TOKEN }} 28 | file: info.lcov 29 | fail_ci_if_error: true 30 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .build 2 | DerivedData 3 | /.previous-build 4 | xcuserdata 5 | .DS_Store 6 | *~ 7 | \#* 8 | .\#* 9 | .*.sw[nop] 10 | *.xcscmblueprint 11 | /default.profraw 12 | *.xcodeproj 13 | Utilities/Docker/*.tar.gz 14 | .swiftpm 15 | Package.resolved 16 | /build 17 | *.pyc 18 | -------------------------------------------------------------------------------- /Assets/logo/logo.fig: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ptrkstr/Devices/eba706e54304c4a92193da4c939e3d7090c89cc3/Assets/logo/logo.fig -------------------------------------------------------------------------------- /Assets/logo/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /Generator/Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version:5.5 2 | // The swift-tools-version declares the minimum version of Swift required to build this package. 3 | 4 | import PackageDescription 5 | 6 | let package = Package( 7 | name: "Generator", 8 | 9 | dependencies: [ 10 | .package(url: "https://github.com/scinfu/SwiftSoup", .upToNextMajor(from: "2.0.0")), 11 | .package(url: "https://github.com/ptrkstr/Slab", .upToNextMajor(from: "1.0.0")), 12 | .package(url: "https://github.com/apple/swift-collections", .upToNextMajor(from: "1.0.0")), 13 | .package(path: "../") // Devices 14 | ], 15 | targets: [ 16 | // Targets are the basic building blocks of a package. A target can define a module or a test suite. 17 | // Targets can depend on other targets in this package, and on products in packages this package depends on. 18 | .executableTarget( 19 | name: "Generator", 20 | dependencies: [ 21 | "Devices", 22 | "SwiftSoup", 23 | "Slab", 24 | .product(name: "Collections", package: "swift-collections") 25 | ], 26 | resources: [ 27 | .process("Resources") 28 | ] 29 | ) 30 | ] 31 | ) 32 | -------------------------------------------------------------------------------- /Generator/README.md: -------------------------------------------------------------------------------- 1 | # Generator 2 | 3 | A description of this package. 4 | -------------------------------------------------------------------------------- /Generator/Sources/Generator/main.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import SwiftSoup 3 | import Slab 4 | import Devices 5 | import OrderedCollections 6 | 7 | // TODO: Retrieve image from Frames? 8 | // TODO: Get content from wikipedia? 9 | // TODO: Flag to generate from local file 10 | // TODO: Pass mocks to generator 11 | 12 | private struct Generator { 13 | 14 | private let slab = Slab() 15 | private let decoder = JSONDecoder() 16 | private let currentDirectory = URL(fileURLWithPath: #file, isDirectory: false).deletingLastPathComponent() 17 | 18 | func generate() throws { 19 | 20 | let newLocation = URL(string: "https://www.theiphonewiki.com/wiki/Models")! 21 | log("Downloading latest data from \(newLocation)") 22 | let newString = try String(contentsOf: newLocation, encoding: .utf8) 23 | let newDocument = try SwiftSoup.parse(newString) 24 | let newTables = try newDocument.getElementsByTag("table") 25 | var devices: [DeviceType] = [] 26 | 27 | let storedLocation = Bundle.module.resourceURL!.appendingPathComponent("Models - The iPhone Wiki.html") 28 | if FileManager.default.fileExists(atPath: storedLocation.path) { 29 | let storedString = try String(contentsOf: storedLocation, encoding: .utf8) 30 | let storedDocument = try SwiftSoup.parse(storedString) 31 | let storedTables = try storedDocument.getElementsByTag("table") 32 | 33 | guard try newTables.outerHtml() != storedTables.outerHtml() else { 34 | return log("No changes detected since last download") 35 | } 36 | } 37 | 38 | var output = 39 | """ 40 | // Generated on \(Date()) 41 | // Manual modifications will be overwitten. 42 | """ 43 | 44 | try write(to: &output, from: newTables, at: 1, for: Airpod.self, devices: &devices) 45 | try write(to: &output, from: newTables, at: 2, for: Airtag.self, devices: &devices) 46 | try write(to: &output, from: newTables, at: 3, for: AppleTV.self, devices: &devices) 47 | try write(to: &output, from: newTables, at: 4, for: SiriRemote.self, devices: &devices) 48 | try write(to: &output, from: newTables, at: 5, for: AppleWatch.self, devices: &devices) 49 | try write(to: &output, from: newTables, at: 6, for: HomePod.self, devices: &devices) 50 | try write(to: &output, from: newTables, at: 7, for: iPad.self, devices: &devices) 51 | try write(to: &output, from: newTables, at: 8, for: ApplePencil.self, devices: &devices) 52 | try write(to: &output, from: newTables, at: 9, for: SmartKeyboard.self, devices: &devices) 53 | try write(to: &output, from: newTables, at: 10, for: iPadAir.self, devices: &devices) 54 | try write(to: &output, from: newTables, at: 11, for: iPadPro.self, devices: &devices) 55 | try write(to: &output, from: newTables, at: 12, for: iPadMini.self, devices: &devices) 56 | try write(to: &output, from: newTables, at: 13, for: iPhone.self, devices: &devices) 57 | try write(to: &output, from: newTables, at: 14, for: iPodTouch.self, devices: &devices) 58 | try write(to: &output, from: newTables, at: 15, for: iMac.self, devices: &devices) 59 | try write(to: &output, from: newTables, at: 16, for: MacMini.self, devices: &devices) 60 | try write(to: &output, from: newTables, at: 17, for: MacBookAir.self, devices: &devices) 61 | try write(to: &output, from: newTables, at: 18, for: MacBookPro.self, devices: &devices) 62 | try write(to: &output, from: devices) 63 | 64 | try persist(output) 65 | try persistWiki(newString) 66 | } 67 | 68 | private func write(to string: inout String, from tables: Elements, at index: Int, for type: T.Type, devices: inout [DeviceType]) throws { 69 | 70 | log("Parsing \(type)") 71 | 72 | let array = try slab.convert( 73 | tables[index].outerHtml(), 74 | configuration: 75 | .init( 76 | modify: { element, row, col in 77 | try element.select("sup").remove() 78 | return element 79 | } 80 | ) 81 | ) 82 | let data = try JSONSerialization.data(withJSONObject: array, options: []) 83 | let objects = try decoder.decode([T].self, from: data) 84 | devices.append(contentsOf: objects) 85 | 86 | string.append( 87 | """ 88 | 89 | 90 | public extension \(type) { 91 | static var all: [\(type)] { 92 | [ 93 | \(try { 94 | try objects.map { "\t\t\t\(try encode($0))" }.joined(separator: ",\n") 95 | }()) 96 | ] 97 | } 98 | } 99 | """ 100 | ) 101 | } 102 | 103 | private func write(to string: inout String, from devices: [DeviceType]) throws { 104 | 105 | log("Writing all") 106 | 107 | let dictionary = OrderedDictionary(grouping: devices, by: { $0.identifier }) 108 | .reduce(OrderedDictionary()) { dictionary, pair in 109 | var dictionary = dictionary 110 | pair.key.components(separatedBy: "\\n").forEach { 111 | dictionary[String($0)] = pair.value 112 | } 113 | return dictionary 114 | } 115 | 116 | 117 | 118 | string.append( 119 | """ 120 | 121 | 122 | public struct DeviceList { 123 | \tpublic let all: [Identifier: [DeviceType]] = [ 124 | \(try { 125 | try dictionary.map { group in 126 | """ 127 | \t\t\"\(group.key)\": [ 128 | \(try { 129 | try group.value.map { "\t\t\t\(try encode($0))" }.joined(separator: ",\n") 130 | }()) 131 | \t\t] 132 | """ 133 | }.joined(separator: ",\n") 134 | }()) 135 | \t] 136 | } 137 | """ 138 | ) 139 | 140 | } 141 | 142 | private func persistWiki(_ string: String) throws { 143 | log("Saving `Models - The iPhone Wiki.html`") 144 | let ouputDirectory = currentDirectory.appendingPathComponent("Resources/Models - The iPhone Wiki.html") 145 | let data = string.data(using: .utf8)! 146 | try data.write(to: ouputDirectory) 147 | } 148 | 149 | private func persist(_ string: String) throws { 150 | log("Saving `Devices.swift`") 151 | let ouputDirectory = currentDirectory.appendingPathComponent("../../../Sources/Devices/Devices.swift") 152 | let data = string.data(using: .utf8)! 153 | try data.write(to: ouputDirectory) 154 | } 155 | 156 | private func encode(_ object: Any) throws -> String { 157 | let objectReflection = Mirror(reflecting: object) 158 | let params = try objectReflection.children.map { child in 159 | let value: String = try { 160 | switch child.value { 161 | case let array as [String]: return "[\(array.map { "\"\($0)\"" }.joined(separator: ", "))]" 162 | case let string as String: return "\"\(string)\"" 163 | default: throw NSError(domain: "", code: 0, userInfo: [NSLocalizedDescriptionKey: "Unknown type found during encoding"]) 164 | } 165 | }() 166 | return "\(child.label!): \(value)" 167 | } 168 | .joined(separator: ", ") 169 | 170 | return "\(type(of: object))(\(params))" 171 | } 172 | } 173 | 174 | func log(_ string: String) { 175 | print("Devices Generator: \(string)") 176 | } 177 | 178 | do { 179 | log("Started") 180 | try Generator().generate() 181 | log("Finished") 182 | exit(EXIT_SUCCESS) 183 | } catch { 184 | log("ERROR - \(error.localizedDescription)") 185 | exit(EXIT_FAILURE) 186 | } 187 | RunLoop.main.run() 188 | -------------------------------------------------------------------------------- /Generator/Tests/GeneratorTests/GeneratorTests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | @testable import Generator 3 | 4 | final class GeneratorTests: XCTestCase { 5 | func testExample() throws { 6 | 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 ptrkstr 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version:5.0 2 | // The swift-tools-version declares the minimum version of Swift required to build this package. 3 | 4 | import PackageDescription 5 | 6 | let package = Package( 7 | name: "Devices", 8 | products: [ 9 | // Products define the executables and libraries a package produces, and make them visible to other packages. 10 | .library( 11 | name: "Devices", 12 | targets: ["Devices"]), 13 | ], 14 | targets: [ 15 | // Targets are the basic building blocks of a package. A target can define a module or a test suite. 16 | // Targets can depend on other targets in this package, and on products in packages this package depends on. 17 | .target( 18 | name: "Devices" 19 | ), 20 | .testTarget( 21 | name: "DevicesTests", 22 | dependencies: ["Devices"] 23 | ) 24 | ] 25 | ) 26 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 🚨 No Longer Maintained 🚨 2 | 3 | - The website this depends on has changed, breaking the daily CI check. 4 | - Some time would be needed to figure out the issue. 5 | - This is a project with no sponsorship and so I don't have time to fix things breaking. 6 | 7 | --- 8 | 9 |
10 | 11 |

12 | Devices 13 |

14 |
15 |
16 |

17 | 18 | 19 |
20 | 21 | 22 | 23 |

24 |
25 |

26 | Swift package that contains all devices from https://www.theiphonewiki.com/wiki/Models. A common use case is wanting to convert device identifiers (also known as machine identifiers) such as iPhone10,1 to a user friendly name; iPhone 8. 27 |

28 |
29 | 30 | 31 | ## Features 32 | 33 | - All Apple Devices 34 | - 🎧 AirPods 35 | - ⚪️ AirTags 36 | - 📺 AppleTVs 37 | - SiriRemotes 38 | - ⌚️ Apple Watches 39 | - 🏠 HomePods 40 | - 🔲 iPads 41 | - ✏️ Apple Pencils 42 | - ⌨️ Smart Keyboard 43 | - 📱 iPhones 44 | - 📱 iPod Touches 45 | - 💻 Macs 46 | - 📝 Provides device information on: 47 | - Generation - iPhone XR 48 | - Bootroom - Bootrom 3865.0.0.4.7 49 | - Internal Name - N841AP 50 | - Identifier - iPhone11,8 51 | - Storage - 64 GB 52 | - Color/Finish - Black 53 | - Model - MRY42 54 | - more! 55 | - 🕒 Checks for new devices every day. 56 | - 🔌 No networking, runs offline. 57 | 58 | ## Usage 59 | 60 | Each device has an `all` property. Use this to find, filter, map etc. The following are some examples. 61 | 62 | ### Find the generation of current device 63 | 64 | ```swift 65 | let identifier = "iPad3,6" 66 | let iPhone = iPhone.all.first { $0.identifier == identifier } 67 | iPhone.generation // iPad (4th generation) 68 | ``` 69 | 70 | ### Find the generation of current device using subscript 71 | 72 | ```swift 73 | let identifier = "iPad3,6" 74 | let iPhone = DeviceList().all[identifer].first! 75 | iPhone.generation // iPad (4th generation) 76 | ``` 77 | 78 | ### List all available colors of the iPad Air 2 64 GB 79 | 80 | ```swift 81 | let colors = iPadAir.all.filter { 82 | $0.generation == "iPad Air 2" && 83 | $0.storage == "64 GB" 84 | }.map { $0.finish } 85 | Set(colors).sorted() // ["Gold", "Silver", "Space Gray"] 86 | ``` 87 | 88 | ### List all Apple Watch Identifiers 89 | 90 | ```swift 91 | let identifiers = AppleWatch.all.map { $0.identifier } 92 | Set(identifiers).sorted() // ["Watch1,1", "Watch1,2", "Watch2,3", ...] 93 | ``` 94 | 95 | ### List all models of the iPad mini 2, iPad4,5, Silver 16 GB 96 | 97 | ```swift 98 | let iPad = iPadMini.all.first { 99 | $0.identifier == "iPad4,5" && 100 | $0.finish == "Silver" && 101 | $0.storage == "16 GB" 102 | }! 103 | iPad.model.components(separatedBy: ", ") // ["ME814", "ME818", "MF074", "MF075", "MF076", "MF544"] 104 | ``` 105 | 106 | See FAQ for why `Set` is used in some examples. 107 | 108 | ## Installation 109 | 110 | ### SPM 111 | Add the following to your project: 112 | ``` 113 | https://github.com/ptrkstr/Devices 114 | ``` 115 | 116 | ## FAQ 117 | 118 | ### What are the list of devices and properties I can access? 119 | 120 | You can see them by going to [Types](/Sources/Devices/Types). 121 | 122 | ### Why can I see duplicated devices? I.e. iPad Air has 3 sets of Silver 16 GB. 123 | 124 | That's because a lot of device models are released slightly differently depending on the region i.e. in China, iPhones can come with two sim card slots as opposed to western regions which either include 1 slot [and an esim]. Those different devices tend to have a different "A" number, FCC ID, Identifier and Model. If you're performing searches for Finish/Color or Storage, you may want to remove duplicates. 125 | 126 | ### Why are some of the returned values a question mark or Unknown? 127 | 128 | The data may not (yet) be available. 129 | 130 | ### How can I split values that contains commas or newlines? 131 | 132 | The model field sometimes contains multiple model names. These can be split with `.model.components(separatedBy: ", ")` 133 | The identifier field sometimes contains multiple identifiers. These can be split with `.identifier.components(separatedBy: "\n")` 134 | 135 | ### How quickly will this update when Apple releases new devices? 136 | 137 | A check is run every day to see if [this](https://www.theiphonewiki.com/wiki/Models) list of devices has changed. If so, a pull request is raised and I'll be notified to review it. You can expect a delay of a few days at the very most. 138 | 139 | ## Alternatives 140 | 141 | - [How to determine the current iPhone/device model?](https://stackoverflow.com/questions/26028918/how-to-determine-the-current-iphone-device-model) 142 | - [How to get device make and model on iOS?](https://stackoverflow.com/questions/11197509/how-to-get-device-make-and-model-on-ios) 143 | -------------------------------------------------------------------------------- /Sources/Devices/DeviceType.swift: -------------------------------------------------------------------------------- 1 | public typealias Identifier = String 2 | public typealias Generation = String 3 | 4 | public protocol DeviceType: Decodable { 5 | var identifier: Identifier { get } 6 | var generation: Generation { get } 7 | } 8 | 9 | 10 | -------------------------------------------------------------------------------- /Sources/Devices/Extensions/UIDevice+Extension.swift: -------------------------------------------------------------------------------- 1 | //#if canImport(UIKit) 2 | // 3 | //import UIKit 4 | // 5 | //public extension UIDevice { 6 | // var identifier: String { 7 | // var systemInfo = utsname() 8 | // uname(&systemInfo) 9 | // let machineMirror = Mirror(reflecting: systemInfo.machine) 10 | // let identifier = machineMirror.children.reduce("") { identifier, element in 11 | // guard let value = element.value as? Int8, value != 0 else { return identifier } 12 | // return identifier + String(UnicodeScalar(UInt8(value))) 13 | // } 14 | // return identifier 15 | // } 16 | //} 17 | // 18 | //#endif 19 | -------------------------------------------------------------------------------- /Sources/Devices/Types/Airpod.swift: -------------------------------------------------------------------------------- 1 | public struct Airpod: Decodable, DeviceType { 2 | public let generation: Generation 3 | public let aNumber: String 4 | public let bootrom: String 5 | public let fccID: String 6 | public let internalName: String 7 | public let identifier: Identifier 8 | public let model: String 9 | 10 | enum CodingKeys: String, CodingKey { 11 | case generation = "Generation" 12 | case aNumber = "\"A\" Number" 13 | case bootrom = "Bootrom" 14 | case fccID = "FCC ID" 15 | case internalName = "Internal Name" 16 | case identifier = "Identifier" 17 | case model = "Model" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Sources/Devices/Types/Airtag.swift: -------------------------------------------------------------------------------- 1 | public struct Airtag: Decodable, DeviceType { 2 | public let generation: Generation 3 | public let aNumber: String 4 | public let bootrom: String 5 | public let fccID: String 6 | public let internalName: String 7 | public let identifier: Identifier 8 | public let packSize: String 9 | public let model: String 10 | 11 | enum CodingKeys: String, CodingKey { 12 | case generation = "Generation" 13 | case aNumber = "\"A\" Number" 14 | case bootrom = "Bootrom" 15 | case fccID = "FCC ID" 16 | case internalName = "Internal Name" 17 | case identifier = "Identifier" 18 | case packSize = "Pack Size" 19 | case model = "Model" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Sources/Devices/Types/ApplePencil.swift: -------------------------------------------------------------------------------- 1 | public struct ApplePencil: Decodable, DeviceType { 2 | public let generation: Generation 3 | public let aNumber: String 4 | public let fccID: String 5 | public let internalName: String 6 | public let identifier: Identifier 7 | public let model: String 8 | 9 | enum CodingKeys: String, CodingKey { 10 | case generation = "Generation" 11 | case aNumber = "\"A\" Number" 12 | case fccID = "FCC ID" 13 | case internalName = "Internal Name" 14 | case identifier = "Identifier" 15 | case model = "Model" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Sources/Devices/Types/AppleTV.swift: -------------------------------------------------------------------------------- 1 | public struct AppleTV: Decodable, DeviceType { 2 | public let generation: Generation 3 | public let aNumber: String 4 | public let bootrom: String 5 | public let fccID: String 6 | public let internalName: String 7 | public let identifier: Identifier 8 | public let color: String 9 | public let storage: String 10 | public let model: String 11 | 12 | enum CodingKeys: String, CodingKey { 13 | case generation = "Generation" 14 | case aNumber = "\"A\" Number" 15 | case bootrom = "Bootrom" 16 | case fccID = "FCC ID" 17 | case internalName = "Internal Name" 18 | case identifier = "Identifier" 19 | case color = "Color" 20 | case storage = "Storage" 21 | case model = "Model" 22 | } 23 | } 24 | 25 | -------------------------------------------------------------------------------- /Sources/Devices/Types/AppleWatch.swift: -------------------------------------------------------------------------------- 1 | public struct AppleWatch: Decodable, DeviceType { 2 | public let generation: Generation 3 | public let aNumber: String 4 | public let bootrom: String 5 | public let fccID: String 6 | public let internalName: String 7 | public let identifier: Identifier 8 | public let caseMaterial: String 9 | public let finish: String 10 | public let caseSize: String 11 | public let model: String 12 | 13 | enum CodingKeys: String, CodingKey { 14 | case generation = "Generation" 15 | case aNumber = "\"A\" Number" 16 | case bootrom = "Bootrom" 17 | case fccID = "FCC ID" 18 | case internalName = "Internal Name" 19 | case identifier = "Identifier" 20 | case caseMaterial = "Case Material" 21 | case finish = "Finish" 22 | case caseSize = "Case Size" 23 | case model = "Model" 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /Sources/Devices/Types/HomePod.swift: -------------------------------------------------------------------------------- 1 | public struct HomePod: Decodable, DeviceType { 2 | public let generation: Generation 3 | public let aNumber: String 4 | public let bootrom: String 5 | public let fccID: String 6 | public let internalName: String 7 | public let identifier: Identifier 8 | public let color: String 9 | public let model: String 10 | 11 | enum CodingKeys: String, CodingKey { 12 | case generation = "Generation" 13 | case aNumber = "\"A\" Number" 14 | case bootrom = "Bootrom" 15 | case fccID = "FCC ID" 16 | case internalName = "Internal Name" 17 | case identifier = "Identifier" 18 | case color = "Color" 19 | case model = "Model" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Sources/Devices/Types/MacBookAir.swift: -------------------------------------------------------------------------------- 1 | public struct MacBookAir: Decodable, DeviceType { 2 | public let generation: Generation 3 | public let aNumber: String 4 | public let fccID: String 5 | public let internalName: String 6 | public let identifier: Identifier 7 | public let color: String 8 | public let storage: String 9 | public let model: String 10 | 11 | enum CodingKeys: String, CodingKey { 12 | case generation = "Generation" 13 | case aNumber = "\"A\" Number" 14 | case fccID = "FCC ID" 15 | case internalName = "Internal Name" 16 | case identifier = "Identifier" 17 | case color = "Color" 18 | case storage = "Storage" 19 | case model = "Model" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Sources/Devices/Types/MacBookPro.swift: -------------------------------------------------------------------------------- 1 | public struct MacBookPro: Decodable, DeviceType { 2 | public let generation: Generation 3 | public let aNumber: String 4 | public let fccID: String 5 | public let internalName: String 6 | public let identifier: Identifier 7 | public let color: String 8 | public let storage: String 9 | public let model: String 10 | 11 | enum CodingKeys: String, CodingKey { 12 | case generation = "Generation" 13 | case aNumber = "\"A\" Number" 14 | case fccID = "FCC ID" 15 | case internalName = "Internal Name" 16 | case identifier = "Identifier" 17 | case color = "Color" 18 | case storage = "Storage" 19 | case model = "Model" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Sources/Devices/Types/MacMini.swift: -------------------------------------------------------------------------------- 1 | public struct MacMini: Decodable, DeviceType { 2 | public let generation: Generation 3 | public let aNumber: String 4 | public let fccID: String 5 | public let internalName: String 6 | public let identifier: Identifier 7 | public let color: String 8 | public let storage: String 9 | public let model: String 10 | 11 | enum CodingKeys: String, CodingKey { 12 | case generation = "Generation" 13 | case aNumber = "\"A\" Number" 14 | case fccID = "FCC ID" 15 | case internalName = "Internal Name" 16 | case identifier = "Identifier" 17 | case color = "Color" 18 | case storage = "Storage" 19 | case model = "Model" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Sources/Devices/Types/SiriRemote.swift: -------------------------------------------------------------------------------- 1 | public struct SiriRemote: Decodable, DeviceType { 2 | public let generation: Generation 3 | public let aNumber: String 4 | public let fccID: String 5 | public let internalName: String 6 | public let identifier: Identifier 7 | public let model: String 8 | 9 | enum CodingKeys: String, CodingKey { 10 | case generation = "Generation" 11 | case aNumber = "\"A\" Number" 12 | case fccID = "FCC ID" 13 | case internalName = "Internal Name" 14 | case identifier = "Identifier" 15 | case model = "Model" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Sources/Devices/Types/SmartKeyboard.swift: -------------------------------------------------------------------------------- 1 | public struct SmartKeyboard: Decodable, DeviceType { 2 | public let generation: Generation 3 | public let aNumber: String 4 | public let internalName: String 5 | public let identifier: Identifier 6 | public let iPadCompatibility: String 7 | public let model: String 8 | 9 | enum CodingKeys: String, CodingKey { 10 | case generation = "Generation" 11 | case aNumber = "\"A\" Number" 12 | case internalName = "Internal Name" 13 | case identifier = "Identifier" 14 | case iPadCompatibility = "iPad Compatibility" 15 | case model = "Model" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Sources/Devices/Types/iMac.swift: -------------------------------------------------------------------------------- 1 | public struct iMac: Decodable, DeviceType { 2 | public let generation: Generation 3 | public let aNumber: String 4 | public let fccID: String 5 | public let internalName: String 6 | public let identifier: Identifier 7 | public let color: String 8 | public let storage: String 9 | public let model: String 10 | 11 | enum CodingKeys: String, CodingKey { 12 | case generation = "Generation" 13 | case aNumber = "\"A\" Number" 14 | case fccID = "FCC ID" 15 | case internalName = "Internal Name" 16 | case identifier = "Identifier" 17 | case color = "Color" 18 | case storage = "Storage" 19 | case model = "Model" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Sources/Devices/Types/iPad.swift: -------------------------------------------------------------------------------- 1 | public struct iPad: Decodable, DeviceType { 2 | public let generation: Generation 3 | public let aNumber: String 4 | public let bootrom: String 5 | public let fccID: String 6 | public let internalName: String 7 | public let identifier: Identifier 8 | public let finish: String 9 | public let storage: String 10 | public let model: String 11 | 12 | enum CodingKeys: String, CodingKey { 13 | case generation = "Generation" 14 | case aNumber = "\"A\" Number" 15 | case bootrom = "Bootrom" 16 | case fccID = "FCC ID" 17 | case internalName = "Internal Name" 18 | case identifier = "Identifier" 19 | case finish = "Finish" 20 | case storage = "Storage" 21 | case model = "Model" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Sources/Devices/Types/iPadAir.swift: -------------------------------------------------------------------------------- 1 | public struct iPadAir: Decodable, DeviceType { 2 | public let generation: Generation 3 | public let aNumber: String 4 | public let bootrom: String 5 | public let fccID: String 6 | public let internalName: String 7 | public let identifier: Identifier 8 | public let finish: String 9 | public let storage: String 10 | public let model: String 11 | 12 | enum CodingKeys: String, CodingKey { 13 | case generation = "Generation" 14 | case aNumber = "\"A\" Number" 15 | case bootrom = "Bootrom" 16 | case fccID = "FCC ID" 17 | case internalName = "Internal Name" 18 | case identifier = "Identifier" 19 | case finish = "Finish" 20 | case storage = "Storage" 21 | case model = "Model" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Sources/Devices/Types/iPadMini.swift: -------------------------------------------------------------------------------- 1 | public struct iPadMini: Decodable, DeviceType { 2 | public let generation: Generation 3 | public let aNumber: String 4 | public let bootrom: String 5 | public let fccID: String 6 | public let internalName: String 7 | public let identifier: Identifier 8 | public let finish: String 9 | public let storage: String 10 | public let model: String 11 | 12 | enum CodingKeys: String, CodingKey { 13 | case generation = "Generation" 14 | case aNumber = "\"A\" Number" 15 | case bootrom = "Bootrom" 16 | case fccID = "FCC ID" 17 | case internalName = "Internal Name" 18 | case identifier = "Identifier" 19 | case finish = "Finish" 20 | case storage = "Storage" 21 | case model = "Model" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Sources/Devices/Types/iPadPro.swift: -------------------------------------------------------------------------------- 1 | public struct iPadPro: Decodable, DeviceType { 2 | public let generation: Generation 3 | public let aNumber: String 4 | public let bootrom: String 5 | public let fccID: String 6 | public let internalName: String 7 | public let identifier: Identifier 8 | public let finish: String 9 | public let storage: String 10 | public let model: String 11 | 12 | enum CodingKeys: String, CodingKey { 13 | case generation = "Generation" 14 | case aNumber = "\"A\" Number" 15 | case bootrom = "Bootrom" 16 | case fccID = "FCC ID" 17 | case internalName = "Internal Name" 18 | case identifier = "Identifier" 19 | case finish = "Finish" 20 | case storage = "Storage" 21 | case model = "Model" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Sources/Devices/Types/iPhone.swift: -------------------------------------------------------------------------------- 1 | public struct iPhone: Decodable, DeviceType { 2 | public let generation: Generation 3 | public let aNumber: String 4 | public let bootrom: String 5 | public let fccID: String 6 | public let internalName: String 7 | public let identifier: Identifier 8 | public let finish: String 9 | public let storage: String 10 | public let model: String 11 | 12 | enum CodingKeys: String, CodingKey { 13 | case generation = "Generation" 14 | case aNumber = "\"A\" Number" 15 | case bootrom = "Bootrom" 16 | case fccID = "FCC ID" 17 | case internalName = "Internal Name" 18 | case identifier = "Identifier" 19 | case finish = "Finish" 20 | case storage = "Storage" 21 | case model = "Model" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Sources/Devices/Types/iPodTouch.swift: -------------------------------------------------------------------------------- 1 | public struct iPodTouch: Decodable, DeviceType { 2 | public let generation: Generation 3 | public let aNumber: String 4 | public let bootrom: String 5 | public let fccID: String 6 | public let internalName: String 7 | public let identifier: Identifier 8 | public let color: String 9 | public let storage: String 10 | public let model: String 11 | 12 | enum CodingKeys: String, CodingKey { 13 | case generation = "Generation" 14 | case aNumber = "\"A\" Number" 15 | case bootrom = "Bootrom" 16 | case fccID = "FCC ID" 17 | case internalName = "Internal Name" 18 | case identifier = "Identifier" 19 | case color = "Color" 20 | case storage = "Storage" 21 | case model = "Model" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Tests/DevicesTests/Devices_Tests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | @testable import Devices 3 | 4 | final class Devices_Tests: XCTestCase { 5 | 6 | func test_properties() { 7 | _ = Airpod.all 8 | _ = Airtag.all 9 | _ = ApplePencil.all 10 | _ = AppleTV.all 11 | _ = AppleWatch.all 12 | _ = HomePod.all 13 | _ = iMac.all 14 | _ = iPad.all 15 | _ = iPadAir.all 16 | _ = iPadMini.all 17 | _ = iPadPro.all 18 | _ = iPhone.all 19 | _ = iPodTouch.all 20 | _ = MacBookAir.all 21 | _ = MacBookPro.all 22 | _ = MacMini.all 23 | _ = SiriRemote.all 24 | _ = SmartKeyboard.all 25 | } 26 | 27 | func test_generation_of_identifier() { 28 | let identifier = "iPhone12,5" 29 | let iPhone = iPhone.all.first { $0.identifier == identifier }! 30 | XCTAssertEqual("iPhone 11 Pro Max", iPhone.generation) 31 | } 32 | 33 | func test_colors_iPadAir2_64GB() { 34 | let colors = iPadAir.all.filter { 35 | $0.generation == "iPad Air 2" && 36 | $0.storage == "64 GB" 37 | }.map { $0.finish } 38 | XCTAssertEqual(["Gold", "Silver", "Space Gray"], Set(colors).sorted()) 39 | } 40 | 41 | func test_appleWatchIdentifiers() { 42 | let identifiers = AppleWatch.all.map { $0.identifier } 43 | XCTAssertEqual( 44 | ["Watch1,1", "Watch1,2", "Watch2,3", "Watch2,4", "Watch2,6", "Watch2,7", "Watch3,1", "Watch3,2", "Watch3,3", "Watch3,4", "Watch4,1", "Watch4,2", "Watch4,3", "Watch4,4", "Watch5,1", "Watch5,10", "Watch5,11", "Watch5,12", "Watch5,2", "Watch5,3", "Watch5,4", "Watch5,9"], 45 | Set(identifiers).sorted().prefix(22) 46 | ) 47 | } 48 | 49 | func test_models_in_iPadMini2_iPad4_5_Silver_16GB() { 50 | let iPad = iPadMini.all.first { 51 | $0.identifier == "iPad4,5" && 52 | $0.finish == "Silver" && 53 | $0.storage == "16 GB" 54 | }! 55 | XCTAssertEqual( 56 | ["ME814", "ME818", "MF074", "MF075", "MF076", "MF544"], 57 | iPad.model.components(separatedBy: ", ") 58 | ) 59 | } 60 | 61 | func test_all() { 62 | let ipad = DeviceList().all["iPad3,6"]!.first! 63 | XCTAssertEqual(ipad.generation, "iPad (4th generation)") 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /Tests/DevicesTests/Types/Airpod_Tests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | @testable import Devices 3 | 4 | final class Airpod_Tests: XCTestCase { 5 | func test_decoding() { 6 | let data = 7 | """ 8 | { 9 | "Generation": "", 10 | "\\"A\\" Number": "", 11 | "Bootrom": "", 12 | "FCC ID": "", 13 | "Internal Name": "", 14 | "Identifier": "", 15 | "Model": "" 16 | } 17 | """ 18 | .data(using: .utf8)! 19 | XCTAssertNoThrow(try JSONDecoder().decode(Airpod.self, from: data)) 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Tests/DevicesTests/Types/Airtag_Tests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | @testable import Devices 3 | 4 | final class Airtag_Tests: XCTestCase { 5 | func test_decoding() { 6 | let data = 7 | """ 8 | { 9 | "Generation": "", 10 | "\\"A\\" Number": "", 11 | "Bootrom": "", 12 | "FCC ID": "", 13 | "Internal Name": "", 14 | "Identifier": "", 15 | "Pack Size": "", 16 | "Model": "" 17 | } 18 | """ 19 | .data(using: .utf8)! 20 | XCTAssertNoThrow(try JSONDecoder().decode(Airtag.self, from: data)) 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Tests/DevicesTests/Types/ApplePencil_Tests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | @testable import Devices 3 | 4 | final class ApplePencil_Tests: XCTestCase { 5 | func test_decoding() { 6 | let data = 7 | """ 8 | { 9 | "Generation": "", 10 | "\\"A\\" Number": "", 11 | "FCC ID": "", 12 | "Internal Name": "", 13 | "Identifier": "", 14 | "Model": "" 15 | } 16 | """ 17 | .data(using: .utf8)! 18 | XCTAssertNoThrow(try JSONDecoder().decode(ApplePencil.self, from: data)) 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Tests/DevicesTests/Types/AppleTV_Tests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | @testable import Devices 3 | 4 | final class AppleTV_Tests: XCTestCase { 5 | func test_decoding() { 6 | let data = 7 | """ 8 | { 9 | "Generation": "", 10 | "\\"A\\" Number": "", 11 | "Bootrom": "", 12 | "FCC ID": "", 13 | "Internal Name": "", 14 | "Identifier": "", 15 | "Color": "", 16 | "Storage": "", 17 | "Model": "" 18 | } 19 | """ 20 | .data(using: .utf8)! 21 | XCTAssertNoThrow(try JSONDecoder().decode(AppleTV.self, from: data)) 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Tests/DevicesTests/Types/AppleWatch_Tests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | @testable import Devices 3 | 4 | final class AppleWatch_Tests: XCTestCase { 5 | func test_decoding() { 6 | let data = 7 | """ 8 | { 9 | "Generation": "", 10 | "\\"A\\" Number": "", 11 | "Bootrom": "", 12 | "FCC ID": "", 13 | "Internal Name": "", 14 | "Identifier": "", 15 | "Case Material": "", 16 | "Finish": "", 17 | "Case Size": "", 18 | "Model": "" 19 | } 20 | """ 21 | .data(using: .utf8)! 22 | XCTAssertNoThrow(try JSONDecoder().decode(AppleWatch.self, from: data)) 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /Tests/DevicesTests/Types/HomePod_Tests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | @testable import Devices 3 | 4 | final class HomePod_Tests: XCTestCase { 5 | func test_decoding() { 6 | let data = 7 | """ 8 | { 9 | "Generation": "", 10 | "\\"A\\" Number": "", 11 | "Bootrom": "", 12 | "FCC ID": "", 13 | "Internal Name": "", 14 | "Identifier": "", 15 | "Color": "", 16 | "Model": "" 17 | } 18 | """ 19 | .data(using: .utf8)! 20 | XCTAssertNoThrow(try JSONDecoder().decode(HomePod.self, from: data)) 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Tests/DevicesTests/Types/MacBookAir_Tests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | @testable import Devices 3 | 4 | final class MacBookAir_Tests: XCTestCase { 5 | func test_decoding() { 6 | let data = 7 | """ 8 | { 9 | "Generation": "", 10 | "\\"A\\" Number": "", 11 | "Bootrom": "", 12 | "FCC ID": "", 13 | "Internal Name": "", 14 | "Identifier": "", 15 | "Color": "", 16 | "Storage": "", 17 | "Model": "" 18 | } 19 | """ 20 | .data(using: .utf8)! 21 | XCTAssertNoThrow(try JSONDecoder().decode(MacBookAir.self, from: data)) 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Tests/DevicesTests/Types/MacBookPro_Tests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | @testable import Devices 3 | 4 | final class MacBookPro_Tests: XCTestCase { 5 | func test_decoding() { 6 | let data = 7 | """ 8 | { 9 | "Generation": "", 10 | "\\"A\\" Number": "", 11 | "Bootrom": "", 12 | "FCC ID": "", 13 | "Internal Name": "", 14 | "Identifier": "", 15 | "Color": "", 16 | "Storage": "", 17 | "Model": "" 18 | } 19 | """ 20 | .data(using: .utf8)! 21 | XCTAssertNoThrow(try JSONDecoder().decode(MacBookPro.self, from: data)) 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Tests/DevicesTests/Types/MacMini_Tests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | @testable import Devices 3 | 4 | final class MacMini_Tests: XCTestCase { 5 | func test_decoding() { 6 | let data = 7 | """ 8 | { 9 | "Generation": "", 10 | "\\"A\\" Number": "", 11 | "Bootrom": "", 12 | "FCC ID": "", 13 | "Internal Name": "", 14 | "Identifier": "", 15 | "Color": "", 16 | "Storage": "", 17 | "Model": "" 18 | } 19 | """ 20 | .data(using: .utf8)! 21 | XCTAssertNoThrow(try JSONDecoder().decode(MacMini.self, from: data)) 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Tests/DevicesTests/Types/SiriRemote_Tests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | @testable import Devices 3 | 4 | final class SiriRemote_Tests: XCTestCase { 5 | func test_decoding() { 6 | let data = 7 | """ 8 | { 9 | "Generation": "", 10 | "\\"A\\" Number": "", 11 | "FCC ID": "", 12 | "Internal Name": "", 13 | "Identifier": "", 14 | "Model": "" 15 | } 16 | """ 17 | .data(using: .utf8)! 18 | XCTAssertNoThrow(try JSONDecoder().decode(SiriRemote.self, from: data)) 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Tests/DevicesTests/Types/SmartKeyboard_Tests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | @testable import Devices 3 | 4 | final class SmartKeyboard_Tests: XCTestCase { 5 | func test_decoding() { 6 | let data = 7 | """ 8 | { 9 | "Generation": "", 10 | "\\"A\\" Number": "", 11 | "Internal Name": "", 12 | "Identifier": "", 13 | "iPad Compatibility": "", 14 | "Model": "" 15 | } 16 | """ 17 | .data(using: .utf8)! 18 | XCTAssertNoThrow(try JSONDecoder().decode(SmartKeyboard.self, from: data)) 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Tests/DevicesTests/Types/iMac_Tests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | @testable import Devices 3 | 4 | final class iMac_Tests: XCTestCase { 5 | func test_decoding() { 6 | let data = 7 | """ 8 | { 9 | "Generation": "", 10 | "\\"A\\" Number": "", 11 | "Bootrom": "", 12 | "FCC ID": "", 13 | "Internal Name": "", 14 | "Identifier": "", 15 | "Color": "", 16 | "Storage": "", 17 | "Model": "" 18 | } 19 | """ 20 | .data(using: .utf8)! 21 | XCTAssertNoThrow(try JSONDecoder().decode(iMac.self, from: data)) 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Tests/DevicesTests/Types/iPadAir_Tests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | @testable import Devices 3 | 4 | final class iPadAir_Tests: XCTestCase { 5 | func test_decoding() { 6 | let data = 7 | """ 8 | { 9 | "Generation": "", 10 | "\\"A\\" Number": "", 11 | "Bootrom": "", 12 | "FCC ID": "", 13 | "Internal Name": "", 14 | "Identifier": "", 15 | "Finish": "", 16 | "Storage": "", 17 | "Model": "" 18 | } 19 | """ 20 | .data(using: .utf8)! 21 | XCTAssertNoThrow(try JSONDecoder().decode(iPadAir.self, from: data)) 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Tests/DevicesTests/Types/iPadMini_Tests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | @testable import Devices 3 | 4 | final class iPadMini_Tests: XCTestCase { 5 | func test_decoding() { 6 | let data = 7 | """ 8 | { 9 | "Generation": "", 10 | "\\"A\\" Number": "", 11 | "Bootrom": "", 12 | "FCC ID": "", 13 | "Internal Name": "", 14 | "Identifier": "", 15 | "Finish": "", 16 | "Storage": "", 17 | "Model": "" 18 | } 19 | """ 20 | .data(using: .utf8)! 21 | XCTAssertNoThrow(try JSONDecoder().decode(iPadMini.self, from: data)) 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Tests/DevicesTests/Types/iPadPro_Tests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | @testable import Devices 3 | 4 | final class iPadPro_Tests: XCTestCase { 5 | func test_decoding() { 6 | let data = 7 | """ 8 | { 9 | "Generation": "", 10 | "\\"A\\" Number": "", 11 | "Bootrom": "", 12 | "FCC ID": "", 13 | "Internal Name": "", 14 | "Identifier": "", 15 | "Finish": "", 16 | "Storage": "", 17 | "Model": "" 18 | } 19 | """ 20 | .data(using: .utf8)! 21 | XCTAssertNoThrow(try JSONDecoder().decode(iPadPro.self, from: data)) 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Tests/DevicesTests/Types/iPad_Tests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | @testable import Devices 3 | 4 | final class iPad_Tests: XCTestCase { 5 | func test_decoding() { 6 | let data = 7 | """ 8 | { 9 | "Generation": "", 10 | "\\"A\\" Number": "", 11 | "Bootrom": "", 12 | "FCC ID": "", 13 | "Internal Name": "", 14 | "Identifier": "", 15 | "Finish": "", 16 | "Storage": "", 17 | "Model": "" 18 | } 19 | """ 20 | .data(using: .utf8)! 21 | XCTAssertNoThrow(try JSONDecoder().decode(iPad.self, from: data)) 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Tests/DevicesTests/Types/iPhone_Tests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | @testable import Devices 3 | 4 | final class iPhone_Tests: XCTestCase { 5 | func test_decoding() { 6 | let data = 7 | """ 8 | { 9 | "Generation": "", 10 | "\\"A\\" Number": "", 11 | "Bootrom": "", 12 | "FCC ID": "", 13 | "Internal Name": "", 14 | "Identifier": "", 15 | "Finish": "", 16 | "Storage": "", 17 | "Model": "" 18 | } 19 | """ 20 | .data(using: .utf8)! 21 | XCTAssertNoThrow(try JSONDecoder().decode(iPhone.self, from: data)) 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Tests/DevicesTests/Types/iPodTouch_Tests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | @testable import Devices 3 | 4 | final class iPodTouch_Tests: XCTestCase { 5 | func test_decoding() { 6 | let data = 7 | """ 8 | { 9 | "Generation": "", 10 | "\\"A\\" Number": "", 11 | "Bootrom": "", 12 | "FCC ID": "", 13 | "Internal Name": "", 14 | "Identifier": "", 15 | "Color": "", 16 | "Storage": "", 17 | "Model": "" 18 | } 19 | """ 20 | .data(using: .utf8)! 21 | XCTAssertNoThrow(try JSONDecoder().decode(iPodTouch.self, from: data)) 22 | } 23 | } 24 | --------------------------------------------------------------------------------