From a7b5c5e64edb7e454b3e0b1f93cb19b4ecb30c55 Mon Sep 17 00:00:00 2001 From: Brendan Molloy Date: Wed, 7 Dec 2022 13:16:44 +0100 Subject: [PATCH] Update and vendor UIDeviceComplete --- GiellaKeyboard.xcodeproj/project.pbxproj | 60 ++- .../Controllers/KeyboardViewController.swift | 5 +- Keyboard/Models/KeyboardDefinition.swift | 1 - Keyboard/Models/SystemKeys.swift | 2 +- Keyboard/Models/Theme.swift | 1 - Keyboard/Utility/Utils.swift | 1 - Keyboard/Views/KeyView.swift | 1 - Podfile | 2 - Podfile.lock | 10 +- Shared/UIDeviceComplete/DeviceFamily.swift | 43 ++ Shared/UIDeviceComplete/DeviceModel.swift | 248 ++++++++++++ Shared/UIDeviceComplete/Identifier.swift | 378 ++++++++++++++++++ Shared/UIDeviceComplete/LICENSE | 21 + Shared/UIDeviceComplete/Screen.swift | 93 +++++ Shared/UIDeviceComplete/System.swift | 41 ++ .../UIDeviceComplete/UIDeviceComplete.swift | 45 +++ .../UIDeviceComplete/UIDeviceExtensions.swift | 77 ++++ 17 files changed, 1007 insertions(+), 22 deletions(-) create mode 100644 Shared/UIDeviceComplete/DeviceFamily.swift create mode 100644 Shared/UIDeviceComplete/DeviceModel.swift create mode 100644 Shared/UIDeviceComplete/Identifier.swift create mode 100644 Shared/UIDeviceComplete/LICENSE create mode 100644 Shared/UIDeviceComplete/Screen.swift create mode 100644 Shared/UIDeviceComplete/System.swift create mode 100644 Shared/UIDeviceComplete/UIDeviceComplete.swift create mode 100644 Shared/UIDeviceComplete/UIDeviceExtensions.swift diff --git a/GiellaKeyboard.xcodeproj/project.pbxproj b/GiellaKeyboard.xcodeproj/project.pbxproj index 6322a380..bcb7dda4 100644 --- a/GiellaKeyboard.xcodeproj/project.pbxproj +++ b/GiellaKeyboard.xcodeproj/project.pbxproj @@ -70,6 +70,20 @@ C655E8561EC5559200B12BE4 /* HomeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C655E8551EC5559200B12BE4 /* HomeView.swift */; }; C655E85A1EC558D000B12BE4 /* HomeController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C655E8591EC558D000B12BE4 /* HomeController.swift */; }; C655E85D1EC55D0800B12BE4 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = C655E85B1EC55D0800B12BE4 /* Localizable.strings */; }; + C66222342940B953005AE505 /* DeviceModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = C662222D2940B953005AE505 /* DeviceModel.swift */; }; + C66222352940B953005AE505 /* UIDeviceExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = C662222E2940B953005AE505 /* UIDeviceExtensions.swift */; }; + C66222362940B953005AE505 /* DeviceFamily.swift in Sources */ = {isa = PBXBuildFile; fileRef = C662222F2940B953005AE505 /* DeviceFamily.swift */; }; + C66222372940B953005AE505 /* System.swift in Sources */ = {isa = PBXBuildFile; fileRef = C66222302940B953005AE505 /* System.swift */; }; + C66222382940B953005AE505 /* Identifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = C66222312940B953005AE505 /* Identifier.swift */; }; + C66222392940B953005AE505 /* UIDeviceComplete.swift in Sources */ = {isa = PBXBuildFile; fileRef = C66222322940B953005AE505 /* UIDeviceComplete.swift */; }; + C662223A2940B953005AE505 /* Screen.swift in Sources */ = {isa = PBXBuildFile; fileRef = C66222332940B953005AE505 /* Screen.swift */; }; + C662223B2940B959005AE505 /* Screen.swift in Sources */ = {isa = PBXBuildFile; fileRef = C66222332940B953005AE505 /* Screen.swift */; }; + C662223C2940B959005AE505 /* DeviceModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = C662222D2940B953005AE505 /* DeviceModel.swift */; }; + C662223D2940B959005AE505 /* UIDeviceComplete.swift in Sources */ = {isa = PBXBuildFile; fileRef = C66222322940B953005AE505 /* UIDeviceComplete.swift */; }; + C662223E2940B959005AE505 /* UIDeviceExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = C662222E2940B953005AE505 /* UIDeviceExtensions.swift */; }; + C662223F2940B959005AE505 /* DeviceFamily.swift in Sources */ = {isa = PBXBuildFile; fileRef = C662222F2940B953005AE505 /* DeviceFamily.swift */; }; + C66222402940B959005AE505 /* Identifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = C66222312940B953005AE505 /* Identifier.swift */; }; + C66222412940B959005AE505 /* System.swift in Sources */ = {isa = PBXBuildFile; fileRef = C66222302940B953005AE505 /* System.swift */; }; C6629244254B302000620D6F /* FeatureFlag.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6629243254B302000620D6F /* FeatureFlag.swift */; }; C6629251254B319D00620D6F /* FeatureFlag.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6629243254B302000620D6F /* FeatureFlag.swift */; }; C676AD5B1EC9679000523FF3 /* InstructionsController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C676AD5A1EC9679000523FF3 /* InstructionsController.swift */; }; @@ -247,6 +261,13 @@ C655E8551EC5559200B12BE4 /* HomeView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HomeView.swift; sourceTree = ""; }; C655E8591EC558D000B12BE4 /* HomeController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HomeController.swift; sourceTree = ""; }; C655E85C1EC55D0800B12BE4 /* Base */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = Base; path = HostingApp/Base.lproj/Localizable.strings; sourceTree = ""; }; + C662222D2940B953005AE505 /* DeviceModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DeviceModel.swift; sourceTree = ""; }; + C662222E2940B953005AE505 /* UIDeviceExtensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIDeviceExtensions.swift; sourceTree = ""; }; + C662222F2940B953005AE505 /* DeviceFamily.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DeviceFamily.swift; sourceTree = ""; }; + C66222302940B953005AE505 /* System.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = System.swift; sourceTree = ""; }; + C66222312940B953005AE505 /* Identifier.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Identifier.swift; sourceTree = ""; }; + C66222322940B953005AE505 /* UIDeviceComplete.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIDeviceComplete.swift; sourceTree = ""; }; + C66222332940B953005AE505 /* Screen.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Screen.swift; sourceTree = ""; }; C6629243254B302000620D6F /* FeatureFlag.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeatureFlag.swift; sourceTree = ""; }; C676AD5A1EC9679000523FF3 /* InstructionsController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = InstructionsController.swift; sourceTree = ""; }; C676AD5E1EC985EB00523FF3 /* Strings.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Strings.swift; sourceTree = ""; }; @@ -378,6 +399,7 @@ 4E807D8A19461D9000D875D1 = { isa = PBXGroup; children = ( + C66222282940B722005AE505 /* Shared */, 4E807D9519461D9000D875D1 /* HostingApp */, 4E807DBA19461DC700D875D1 /* Keyboard */, C6F249301F736DCE00840F2B /* BaseKeyboard */, @@ -558,6 +580,28 @@ path = Pods; sourceTree = ""; }; + C66222282940B722005AE505 /* Shared */ = { + isa = PBXGroup; + children = ( + C662222C2940B921005AE505 /* UIDeviceComplete */, + ); + path = Shared; + sourceTree = ""; + }; + C662222C2940B921005AE505 /* UIDeviceComplete */ = { + isa = PBXGroup; + children = ( + C662222F2940B953005AE505 /* DeviceFamily.swift */, + C662222D2940B953005AE505 /* DeviceModel.swift */, + C66222312940B953005AE505 /* Identifier.swift */, + C66222332940B953005AE505 /* Screen.swift */, + C66222302940B953005AE505 /* System.swift */, + C66222322940B953005AE505 /* UIDeviceComplete.swift */, + C662222E2940B953005AE505 /* UIDeviceExtensions.swift */, + ); + path = UIDeviceComplete; + sourceTree = ""; + }; C676AD621EC98C0F00523FF3 /* Controllers */ = { isa = PBXGroup; children = ( @@ -896,7 +940,6 @@ "${PODS_ROOT}/Target Support Files/Pods-HostingApp/Pods-HostingApp-frameworks.sh", "${BUILT_PRODUCTS_DIR}/SQLite.swift/SQLite.framework", "${BUILT_PRODUCTS_DIR}/Sentry/Sentry.framework", - "${BUILT_PRODUCTS_DIR}/UIDeviceComplete/UIDeviceComplete.framework", "${BUILT_PRODUCTS_DIR}/PahkatClient/PahkatClient.framework", "${BUILT_PRODUCTS_DIR}/DivvunSpell/DivvunSpell.framework", "${BUILT_PRODUCTS_DIR}/RxSwift/RxSwift.framework", @@ -905,7 +948,6 @@ outputPaths = ( "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SQLite.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Sentry.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/UIDeviceComplete.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/PahkatClient.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/DivvunSpell.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/RxSwift.framework", @@ -984,7 +1026,9 @@ files = ( 99989FBA2417CFBA00EF8CD3 /* BaseSettingsViewController.swift in Sources */, 9904409523FAC0C50063064E /* DisclosureCell.swift in Sources */, + C66222342940B953005AE505 /* DeviceModel.swift in Sources */, C676AD5B1EC9679000523FF3 /* InstructionsController.swift in Sources */, + C66222362940B953005AE505 /* DeviceFamily.swift in Sources */, 99989FB72417990E00EF8CD3 /* UserDictionary+Test.swift in Sources */, C6B638441EDF81B200A6ECB2 /* SimpleButton.swift in Sources */, C63EE0FF2361D86800A1E15A /* MenuButton.swift in Sources */, @@ -996,16 +1040,21 @@ C655E8541EC5551100B12BE4 /* Extensions.swift in Sources */, C691A0E723682C8B00085AC3 /* TestingController.swift in Sources */, C65430B41EC94BF0007FC053 /* LanguagesController.swift in Sources */, + C66222392940B953005AE505 /* UIDeviceComplete.swift in Sources */, + C66222382940B953005AE505 /* Identifier.swift in Sources */, 999790AC23F2B4E8003F7885 /* UserDictionaryViewController.swift in Sources */, 999790BA23F40E4F003F7885 /* WordContext.swift in Sources */, 999790B623F2F536003F7885 /* WordContextView.swift in Sources */, C65430B01EC94BC7007FC053 /* InstructionsView.swift in Sources */, 4EF16F9B19D2550D0076AFCB /* AppDelegate.swift in Sources */, 997021052416DF8C00A81A27 /* KeyboardLocale+AllLocales.swift in Sources */, + C66222372940B953005AE505 /* System.swift in Sources */, C676AD5F1EC985EB00523FF3 /* Strings.swift in Sources */, C655E85A1EC558D000B12BE4 /* HomeController.swift in Sources */, 99DA1847240D1010003E7353 /* UserDictionaryService.swift in Sources */, 9904409223F84BDC0063064E /* SettingsViewController.swift in Sources */, + C66222352940B953005AE505 /* UIDeviceExtensions.swift in Sources */, + C662223A2940B953005AE505 /* Screen.swift in Sources */, C6A2E01F1EDFAAA2006BAB69 /* AboutView.swift in Sources */, 9904409B23FBDC200063064E /* KeyboardLocale.swift in Sources */, C6996B5722FC2C93007EBC3D /* Utils.swift in Sources */, @@ -1043,6 +1092,7 @@ buildActionMask = 2147483647; files = ( 997550B723EAC631004ED6A5 /* UserDictionary.swift in Sources */, + C66222402940B959005AE505 /* Identifier.swift in Sources */, 99DD30F623E307430065E8D8 /* Audio.swift in Sources */, C6996B5322FB06FA007EBC3D /* DeadKeyHandler.swift in Sources */, C6996B5E22FC3257007EBC3D /* SpellBanner.swift in Sources */, @@ -1050,13 +1100,17 @@ C6996B5C22FC2D3E007EBC3D /* CursorContext.swift in Sources */, C6996B5A22FC2CC7007EBC3D /* SystemKeys.swift in Sources */, C6F249461F736E1400840F2B /* KeyboardDefinition.swift in Sources */, + C662223D2940B959005AE505 /* UIDeviceComplete.swift in Sources */, 99460238243B27140027C1CC /* InputContext.swift in Sources */, 99074EE82422383700075480 /* SpellBannerFlowLayout.swift in Sources */, + C662223E2940B959005AE505 /* UIDeviceExtensions.swift in Sources */, C6629251254B319D00620D6F /* FeatureFlag.swift in Sources */, C6F249471F736E1A00840F2B /* KeyboardViewController.swift in Sources */, 999C30172428E91000E127A6 /* BannerView.swift in Sources */, C61829B8254225B100620D6F /* KeyboardLocale+AllLocales.swift in Sources */, 99DA184424095AF0003E7353 /* UserDictionaryService.swift in Sources */, + C66222412940B959005AE505 /* System.swift in Sources */, + C662223B2940B959005AE505 /* Screen.swift in Sources */, C649D3D4234B7C8000EF4273 /* KeyView.swift in Sources */, 991EE55023D9AB4700DC3677 /* ViewDebugger.swift in Sources */, FDAF59AF22F9CD0700F7C9FC /* KeyOverlayView.swift in Sources */, @@ -1066,6 +1120,7 @@ FDAF59A522F9CCE100F7C9FC /* Theme.swift in Sources */, 999C30132428E6C000E127A6 /* UpdateBanner.swift in Sources */, 99094698243674F20070AEB6 /* SpellerAvailableBanner.swift in Sources */, + C662223F2940B959005AE505 /* DeviceFamily.swift in Sources */, 99074EEB2422387A00075480 /* SpellBannerSeparatorView.swift in Sources */, 99074EE4242230F000075480 /* SpellBannerCell.swift in Sources */, 99C4E56724320F49005F8C8A /* IPC.swift in Sources */, @@ -1074,6 +1129,7 @@ 999C30152428E70600E127A6 /* UpdateBannerView.swift in Sources */, FDBD09C52333A56800303152 /* SplitKeyboard.swift in Sources */, 99074EF62423AAA300075480 /* SpellBannerView.swift in Sources */, + C662223C2940B959005AE505 /* DeviceModel.swift in Sources */, 99074EF224224B4400075480 /* Banner.swift in Sources */, 9930BD90243CD2BF00C0EF7F /* FolderWatcher.swift in Sources */, FDAF59A922F9CCF200F7C9FC /* LongPressController.swift in Sources */, diff --git a/Keyboard/Controllers/KeyboardViewController.swift b/Keyboard/Controllers/KeyboardViewController.swift index e906addf..72842753 100644 --- a/Keyboard/Controllers/KeyboardViewController.swift +++ b/Keyboard/Controllers/KeyboardViewController.swift @@ -1,5 +1,4 @@ import UIKit -import UIDeviceComplete import DivvunSpell protocol KeyboardViewProvider { @@ -87,10 +86,8 @@ open class KeyboardViewController: UIInputViewController { return 405.0 case .iPadPro12_9Inch, .iPadPro12_9Inch_SecondGen, .iPadPro12_9Inch_ThirdGen: return 405.0 - case .iPadSevenGen: - return 405.0 default: - let sizeInches = UIDevice.current.dc.screenSize.sizeInches ?? Screen.maxSupportedInches + let sizeInches = UIDevice.current.dc.screenSize.sizeInches ?? 12.9 if sizeInches < 11 { return landscapeDeviceHeight / 2.0 diff --git a/Keyboard/Models/KeyboardDefinition.swift b/Keyboard/Models/KeyboardDefinition.swift index 3fa60702..ba25b3c5 100644 --- a/Keyboard/Models/KeyboardDefinition.swift +++ b/Keyboard/Models/KeyboardDefinition.swift @@ -1,5 +1,4 @@ import UIKit -import UIDeviceComplete public indirect enum TransformTree: Codable { case tree([String: TransformTree]) diff --git a/Keyboard/Models/SystemKeys.swift b/Keyboard/Models/SystemKeys.swift index 2ea99e79..d5f16d30 100644 --- a/Keyboard/Models/SystemKeys.swift +++ b/Keyboard/Models/SystemKeys.swift @@ -1,6 +1,5 @@ import Foundation import UIKit -import UIDeviceComplete final class SystemKeys { static func systemKeyRowsForCurrentDevice(spaceName: String, @@ -12,6 +11,7 @@ final class SystemKeys { // Left side of space bar if !UIDevice.current.dc.deviceModel.hasNotch { + if isIPad && (UIDevice.current.dc.screenSize.sizeInches ?? Screen.maxSupportedInches) >= 11.0 { keys.append(KeyDefinition(type: .keyboard, size: CGSize(width: 1.25, height: 1.0))) keys.append(KeyDefinition(type: .symbols, size: CGSize(width: 1.25, height: 1.0))) diff --git a/Keyboard/Models/Theme.swift b/Keyboard/Models/Theme.swift index 802cc7ff..a99e7488 100644 --- a/Keyboard/Models/Theme.swift +++ b/Keyboard/Models/Theme.swift @@ -1,6 +1,5 @@ import Foundation import UIKit -import UIDeviceComplete // swiftlint:disable:next type_name struct _Theme { diff --git a/Keyboard/Utility/Utils.swift b/Keyboard/Utility/Utils.swift index b8d22602..f8d3ad94 100644 --- a/Keyboard/Utility/Utils.swift +++ b/Keyboard/Utility/Utils.swift @@ -1,6 +1,5 @@ import Foundation import UIKit -import UIDeviceComplete extension UIDevice { public var systemMajorVersion: Int { diff --git a/Keyboard/Views/KeyView.swift b/Keyboard/Views/KeyView.swift index 940ae4c6..563ea928 100644 --- a/Keyboard/Views/KeyView.swift +++ b/Keyboard/Views/KeyView.swift @@ -1,4 +1,3 @@ -import UIDeviceComplete final class KeyView: UIView { private let key: KeyDefinition diff --git a/Podfile b/Podfile index a7d99216..2034db9b 100644 --- a/Podfile +++ b/Podfile @@ -4,7 +4,6 @@ use_frameworks! target 'BaseKeyboard' do pod 'Sentry', '=7.3.0' pod 'DivvunSpell', :http => "https://github.com/divvun/divvunspell-sdk-swift/releases/download/v1.0.0-beta.3/cargo-pod.tgz" - pod 'UIDeviceComplete', :git => "https://github.com/bbqsrc/UIDeviceComplete" pod 'SwiftLint' pod 'SQLite.swift', '~> 0.12.0' pod 'RxSwift', '~> 5.1.1' @@ -12,7 +11,6 @@ end target 'HostingApp' do pod 'Sentry', '=7.3.0' - pod 'UIDeviceComplete', :git => "https://github.com/bbqsrc/UIDeviceComplete" pod 'SwiftLint' pod 'SQLite.swift', '~> 0.12.0' pod 'PahkatClient', :http => "https://github.com/divvun/pahkat-client-sdk-swift/releases/download/v0.2.0/cargo-pod.tgz" diff --git a/Podfile.lock b/Podfile.lock index 1c38bf65..3f844c0a 100644 --- a/Podfile.lock +++ b/Podfile.lock @@ -9,7 +9,6 @@ PODS: - SQLite.swift/standard (= 0.12.2) - SQLite.swift/standard (0.12.2) - SwiftLint (0.43.1) - - UIDeviceComplete (2.6.6) DEPENDENCIES: - "DivvunSpell (from `{:http=>\"https://github.com/divvun/divvunspell-sdk-swift/releases/download/v1.0.0-beta.3/cargo-pod.tgz\"}`)" @@ -18,7 +17,6 @@ DEPENDENCIES: - Sentry (= 7.3.0) - SQLite.swift (~> 0.12.0) - SwiftLint - - UIDeviceComplete (from `https://github.com/bbqsrc/UIDeviceComplete`) SPEC REPOS: trunk: @@ -32,17 +30,12 @@ EXTERNAL SOURCES: :http: https://github.com/divvun/divvunspell-sdk-swift/releases/download/v1.0.0-beta.3/cargo-pod.tgz PahkatClient: :http: https://github.com/divvun/pahkat-client-sdk-swift/releases/download/v0.2.0/cargo-pod.tgz - UIDeviceComplete: - :git: https://github.com/bbqsrc/UIDeviceComplete CHECKOUT OPTIONS: DivvunSpell: :http: https://github.com/divvun/divvunspell-sdk-swift/releases/download/v1.0.0-beta.3/cargo-pod.tgz PahkatClient: :http: https://github.com/divvun/pahkat-client-sdk-swift/releases/download/v0.2.0/cargo-pod.tgz - UIDeviceComplete: - :commit: af940b2ea4231b3295cd4cb2d59d115afa0d102f - :git: https://github.com/bbqsrc/UIDeviceComplete SPEC CHECKSUMS: DivvunSpell: a49ab7b8f0dba02062619bfe801e371f0671c32a @@ -51,8 +44,7 @@ SPEC CHECKSUMS: Sentry: 9a4e621430e2dae4477d791f2f7e905720b6efbf SQLite.swift: d2b4642190917051ce6bd1d49aab565fe794eea3 SwiftLint: 99f82d07b837b942dd563c668de129a03fc3fb52 - UIDeviceComplete: c89b90393fb2a3446f5e0e86eca0c02d6f8e09f0 -PODFILE CHECKSUM: 3bdac8799e8bb6736f7c98536eb9da1726a1e29d +PODFILE CHECKSUM: 3b18f71f7f09d4be8c66fffcf23c2f4fb2f5b022 COCOAPODS: 1.10.0 diff --git a/Shared/UIDeviceComplete/DeviceFamily.swift b/Shared/UIDeviceComplete/DeviceFamily.swift new file mode 100644 index 00000000..a1af094a --- /dev/null +++ b/Shared/UIDeviceComplete/DeviceFamily.swift @@ -0,0 +1,43 @@ +// +// DeviceFamily.swift +// +// Copyright (c) 2017-2019 Nicholas Maccharoli +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +public enum DeviceFamily: String { + + case iPhone + case iPod + case iPad + case unknown + + public init(rawValue: String) { + switch rawValue { + case "iPhone": + self = .iPhone + case "iPod": + self = .iPod + case "iPad": + self = .iPad + default: + self = .unknown + } + } +} diff --git a/Shared/UIDeviceComplete/DeviceModel.swift b/Shared/UIDeviceComplete/DeviceModel.swift new file mode 100644 index 00000000..4945d85d --- /dev/null +++ b/Shared/UIDeviceComplete/DeviceModel.swift @@ -0,0 +1,248 @@ +// +// DeviceModel.swift +// +// Copyright (c) 2017-2019 Nicholas Maccharoli +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +public enum DeviceModel: CaseIterable { + + case iPhone4, iPhone4S + case iPhone5, iPhone5C, iPhone5S + case iPhone6, iPhone6Plus + case iPhone6S, iPhone6SPlus + case iPhoneSE, iPhoneSE2, iPhoneSE3 + case iPhone7, iPhone7Plus + case iPhone8, iPhone8Plus + case iPhoneX + case iPhoneXS, iPhoneXSMax + case iPhoneXR + case iPhone11 + case iPhone11Pro, iPhone11ProMax + case iPhone12mini, iPhone12 + case iPhone12Pro, iPhone12ProMax + case iPhone13mini, iPhone13 + case iPhone13Pro, iPhone13ProMax + case iPhone14, iPhone14Plus + case iPhone14Pro, iPhone14ProMax + + case iPadFirstGen, iPadSecondGen, iPadThirdGen, iPadFourthGen, iPadFifthGen, iPadSixthGen, iPadSeventhGen, iPadEighthGen, iPadNinthGen, iPadTenthGen + + case iPadAir, iPadAir2, iPadAir3, iPadAir4, iPadAir5 + + case iPadMini, iPadMini2, iPadMini3, iPadMini4, iPadMini5, iPadMini6 + + case iPadPro9_7Inch, iPadPro10_5Inch, iPadPro12_9Inch, iPadPro12_9Inch_SecondGen + + case iPadPro11Inch, iPadPro12_9Inch_ThirdGen + + case iPadPro11Inch_SecondGen, iPadPro12_9Inch_FourthGen + + case iPadPro11Inch_ThirdGen, iPadPro12_9Inch_FifthGen + + case iPadPro11Inch_FourthGen, iPadPro12_9Inch_SixthGen + + case iPodTouchFirstGen, iPodTouchSecondGen, iPodTouchThirdGen, + iPodTouchFourthGen, iPodTouchFifthGen, iPodTouchSixthGen, iPodTouchSeventhGen + + case unknown +} + + +// MARK: - init + +extension DeviceModel { + init(identifier: Identifier) { + switch identifier.type { + case .iPhone: + self = DeviceModel.detectIphoneModel(with: identifier) + case .iPad: + self = DeviceModel.detectIpadModel(with: identifier) + case .iPod: + self = DeviceModel.detectIpodModel(with: identifier) + default: + self = .unknown + } + } +} + + +// MARK: Detecting iPhone Models + +extension DeviceModel { + fileprivate static func detectIphoneModel(with identifier: Identifier) -> DeviceModel { + guard let major = identifier.version.major, + let minor = identifier.version.minor, + identifier.type == .iPhone + else { return .unknown } + + switch (major, minor) { + case (3, _): return .iPhone4 + case (4, _): return .iPhone4S + case (5, 1), (5, 2): return .iPhone5 + + case (5, 3), (5, 4): return .iPhone5C + case (6, _): return .iPhone5S + + case (7, 2): return .iPhone6 + case (7, 1): return .iPhone6Plus + + case (8, 1): return .iPhone6S + case (8, 2): return .iPhone6SPlus + case (8, 4): return .iPhoneSE + + case (9, 1), (9, 3): return .iPhone7 + case (9, 2), (9, 4): return .iPhone7Plus + + case (10, 1), (10, 4): return .iPhone8 + case (10, 2), (10, 5): return .iPhone8Plus + case (10, 3), (10, 6): return .iPhoneX + + case (11, 2): return .iPhoneXS + case (11, 4), (11, 6): return .iPhoneXSMax + case (11, 8): return .iPhoneXR + + case (12, 1): return .iPhone11 + case (12, 3): return .iPhone11Pro + case (12, 5): return .iPhone11ProMax + + case (12, 8): return .iPhoneSE2 + + case (13, 1): return .iPhone12mini + case (13, 2): return .iPhone12 + case (13, 3): return .iPhone12Pro + case (13, 4): return .iPhone12ProMax + + case (14, 4): return .iPhone13mini + case (14, 5): return .iPhone13 + case (14, 2): return .iPhone13Pro + case (14, 3): return .iPhone13ProMax + + case (14, 6): return .iPhoneSE3 + + case (14, 7): return .iPhone14 + case (14, 8): return .iPhone14Plus + + case (15, 2): return .iPhone14Pro + case (15, 3): return .iPhone14ProMax + + default: return .unknown + } + } +} + + +// MARK: Detecting iPad Models + +extension DeviceModel { + fileprivate static func detectIpadModel(with identifier: Identifier) -> DeviceModel { + guard let major = identifier.version.major, + let minor = identifier.version.minor, + identifier.type == .iPad + else { return .unknown } + + switch (major, minor) { + case (1, _): return .iPadFirstGen + case (2, 1), (2, 2), (2, 3), (2, 4): return .iPadSecondGen + case (3, 1), (3, 2), (3, 3): return .iPadThirdGen + case (3, 4), (3, 5), (3, 6): return .iPadFourthGen + case (6, 11), (6, 12): return .iPadFifthGen + case (7, 5), (7, 6): return .iPadSixthGen + case (7, 11), (7, 12): return .iPadSeventhGen + case (11, 6), (11, 7): return .iPadEighthGen + case (12, 1), (12, 2): return .iPadNinthGen + case (13, 18), (13, 19): return .iPadTenthGen + case (4, 1), (4, 2), (4, 3): return .iPadAir + case (5, 3), (5, 4): return .iPadAir2 + case (11, 3), (11, 4): return .iPadAir3 + case (13, 1), (13, 2): return .iPadAir4 + case (13, 16), (13, 17): return .iPadAir5 + case (2, 5), (2, 6), (2, 7): return .iPadMini + case (4, 4), (4, 5), (4, 6): return .iPadMini2 + case (4, 7), (4, 8), (4, 9): return .iPadMini3 + case (5, 1), (5, 2): return .iPadMini4 + case (11, 1), (11, 2): return .iPadMini5 + case (14, 1), (14, 2): return .iPadMini6 + case (6, 3), (6, 4): return .iPadPro9_7Inch + case (7, 3), (7, 4): return .iPadPro10_5Inch + case (8, 1), (8, 2), (8, 3), (8, 4): return .iPadPro11Inch + case (8, 9), (8, 10): return .iPadPro11Inch_SecondGen + case (13, 4), (13, 5), (13, 6), (13, 7): + return .iPadPro11Inch_ThirdGen + case (14, 3), (14, 4): + return .iPadPro11Inch_FourthGen + case (6, 7), (6, 8): return .iPadPro12_9Inch + case (7, 1), (7, 2): return .iPadPro12_9Inch_SecondGen + case (8, 5), (8, 6), (8, 7), (8, 8): return .iPadPro12_9Inch_ThirdGen + case (8, 11), (8, 12): return .iPadPro12_9Inch_FourthGen + case (13, 8), (13, 9), (13, 10), (13, 11): + return .iPadPro12_9Inch_FifthGen + case (14, 5), (14, 6): + return .iPadPro12_9Inch_SixthGen + default: return .unknown + } + } +} + + +// MARK: Detecting iPod Models + +extension DeviceModel { + fileprivate static func detectIpodModel(with identifier: Identifier) -> DeviceModel { + guard let major = identifier.version.major, + let minor = identifier.version.minor, + identifier.type == .iPod + else { return .unknown } + + switch (major, minor) { + case (1, 1): return .iPodTouchFirstGen + case (2, 1): return .iPodTouchSecondGen + case (3, 1): return .iPodTouchThirdGen + case (4, 1): return .iPodTouchFourthGen + case (5, 1): return .iPodTouchFifthGen + case (7, 1): return .iPodTouchSixthGen + case (9, 1): return .iPodTouchSeventhGen + default: return .unknown + } + } +} + + +// MARK: Detecting the Notch + +extension DeviceModel { + public var hasNotch: Bool { + switch self { + + case .iPhoneX, .iPhoneXS, .iPhoneXSMax, .iPhoneXR: + return true + case .iPhone11, .iPhone11Pro, .iPhone11ProMax: + return true + case .iPhone12mini, .iPhone12, .iPhone12Pro, .iPhone12ProMax: + return true + case .iPhone13mini, .iPhone13, .iPhone13Pro, .iPhone13ProMax: + return true + case .iPhone14, .iPhone14Plus, .iPhone14Pro, .iPhone14ProMax: + return true + + default: + return false + } + } +} diff --git a/Shared/UIDeviceComplete/Identifier.swift b/Shared/UIDeviceComplete/Identifier.swift new file mode 100644 index 00000000..dbd4f6ba --- /dev/null +++ b/Shared/UIDeviceComplete/Identifier.swift @@ -0,0 +1,378 @@ +// +// Identifier.swift +// +// Copyright (c) 2017-2019 Nicholas Maccharoli +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +struct Identifier { + let type: DeviceFamily + let version: (major: Int?, minor: Int?) + + init(_ identifier: String) { + let (type, major, minor) = Identifier.typeVersionComponents(with: identifier) + self.type = DeviceFamily(rawValue: type) + self.version = (major, minor) + } +} + + +// MARK: - Identifier String parsing + +extension Identifier { + static func typeVersionComponents(with identifierString: String) -> (type: String, major: Int?, minor: Int?) { + + let numericCharacters: [String] = (0...9).map { "\($0)" } + let type = identifierString.prefix(while: { !numericCharacters.contains(String($0))}) + + let version = identifierString.suffix(from: type.endIndex) + .split(separator: ",") + .map { Int($0) } + + let major: Int? = !version.isEmpty ? version[0] : nil + let minor: Int? = version.count > 1 ? version[1] : nil + + return (String(type), major, minor) + } +} + + +// MARK: - String Representation - iPhone + +extension Identifier: CustomStringConvertible { + var description: String { + + guard let major = version.major, + let minor = version.minor + else { return "unknown" } + + switch type { + case .iPhone: + return iphoneStringRepresentation(major: major, minor: minor) + case .iPad: + return iPadStringRepresentation(major: major, minor: minor) + case .iPod: + return iPodStringRepresentation(major: major, minor: minor) + case .unknown: + return "unknown" + } + } + + private func iphoneStringRepresentation(major: Int, minor: Int) -> String { + switch (major, minor) { + case (1, 1): + return "iPhone" + case (1, 2): + return "iPhone 3G" + case (2, 1): + return "iPhone 3GS" + case (3, 1): + return "iPhone 4" + case (3, 2): + return "iPhone 4 GSM Rev A" + case (3, 3): + return "iPhone 4 CDMA" + case (4, 1): + return "iPhone 4S" + case (5, 1): + return "iPhone 5 GSM+LTE" + case (5, 2): + return "iPhone 5 CDMA+LTE" + case (5, 3): + return "iPhone 5C (GSM)" + case (5, 4): + return "iPhone 5C (Global)" + case (6, 1): + return "iPhone 5S (GSM)" + case (6, 2): + return "iPhone 5S (Global)" + case (7, 1): + return "iPhone 6 Plus" + case (7, 2): + return "iPhone 6" + case (8, 1): + return "iPhone 6s" + case (8, 2): + return "iPhone 6s Plus" + case (8, 3): + return "iPhone SE (GSM+CDMA)" + case (8, 4): + return "iPhone SE (GSM)" + case (9, 1): + return "iPhone 7" + case (9, 2): + return "iPhone 7 Plus" + case (9, 3): + return "iPhone 7" + case (9, 4): + return "iPhone 7 Plus" + case (10, 1): + return "iPhone 8" + case (10, 2): + return "iPhone 8 Plus" + case (10, 3): + return "iPhone X" + case (10, 4): + return "iPhone 8" + case (10, 5): + return "iPhone 8 Plus" + case (10, 6): + return "iPhone X" + case (11, 2): + return "iPhone XS" + case (11, 4): + return "iPhone XS Max" + case (11, 6): + return "iPhone XS Max (China)" + case (11, 8): + return "iPhone XR" + case (12, 1): + return "iPhone 11" + case (12, 3): + return "iPhone 11 Pro" + case (12, 5): + return "iPhone 11 Pro Max" + case (12, 8): + return "iPhone SE (2nd Gen)" + case (13, 1): + return "iPhone 12 mini" + case (13, 2): + return "iPhone 12" + case (13, 3): + return "iPhone 12 Pro" + case (13, 4): + return "iPhone 12 Pro Max" + case (14, 2): + return "iPhone 13 Pro" + case (14, 3): + return "iPhone 13 Pro Max" + case (14, 4): + return "iPhone 13 mini" + case (14, 5): + return "iPhone 13" + + case (14, 6): + return "iPhone SE (3rd Gen)" + + case (14, 7): + return "iPhone 14" + case (14, 8): + return "iPhone 14 Plus" + case (15, 2): + return "iPhone 14 Pro" + case (15, 3): + return "iPhone 14 Pro Max" + + default: + return "unknown" + } + } + + private func iPodStringRepresentation(major: Int, minor: Int) -> String { + switch (major, minor) { + case (1, 1): + return "1st Gen iPod" + case (2, 1): + return "2nd Gen iPod" + case (3, 1): + return "3rd Gen iPod" + case (4, 1): + return "4th Gen iPod" + case (5, 1): + return "5th Gen iPod" + case (7, 1): + return "6th Gen iPod" + case (9, 1): + return "7th Gen iPod" + default: + return "unknown" + } + } + + private func iPadStringRepresentation(major: Int, minor: Int) -> String { + switch (major, minor) { + case (1, 1): + return "iPad" + case (1, 2): + return "iPad 3G" + case (2, 1): + return "2nd Gen iPad" + case (2, 2): + return "2nd Gen iPad GSM" + case (2, 3): + return "2nd Gen iPad CDMA" + case (2, 4): + return "2nd Gen iPad New Revision" + case (2, 5): + return "iPad mini" + case (2, 6): + return "iPad mini GSM+LTE" + case (2, 7): + return "iPad mini CDMA+LTE" + case (3, 1): + return "3rd Gen iPad" + case (3, 2): + return "3rd Gen iPad CDMA" + case (3, 3): + return "3rd Gen iPad GSM" + case (3, 4): + return "4th Gen iPad" + case (3, 5): + return "4th Gen iPad GSM+LTE" + case (3, 6): + return "4th Gen iPad CDMA+LTE" + case (4, 1): + return "iPad Air (WiFi)" + case (4, 2): + return "iPad Air (GSM+CDMA)" + case (4, 3): + return "iPad Air (China)" + case (4, 4): + return "iPad mini Retina (WiFi)" + case (4, 5): + return "iPad mini Retina (GSM+CDMA)" + case (4, 6): + return "iPad mini Retina (China)" + case (4, 7): + return "iPad mini 3 (WiFi)" + case (4, 8): + return "iPad mini 3 (GSM+CDMA)" + case (4, 9): + return "iPad mini 3 (China)" + case (5, 1): + return "iPad mini 4 (WiFi)" + case (5, 2): + return "iPad mini 4 (WiFi+LTE)" + case (5, 3): + return "iPad Air 2 (WiFi)" + case (5, 4): + return "iPad Air 2 (Cellular)" + case (6, 3): + return "iPad Pro (9.7 inch, Wi-Fi)" + case (6, 4): + return "iPad Pro (9.7 inch, Wi-Fi+LTE)" + case (6, 7): + return "iPad Pro (12.9 inch, Wi-Fi)" + case (6, 8): + return "iPad Pro (12.9 inch, Wi-Fi+LTE)" + case (6, 11): + return "5th Gen iPad (WiFi)" + case (6, 12): + return "5th Gen iPad (Cellular)" + case (7, 1): + return "2nd Gen iPad Pro (12.9 inch, Wi-Fi)" + case (7, 2): + return "2nd Gen iPad Pro (12.9 inch, Wi-Fi+LTE)" + case (7, 3): + return "iPad Pro (10.5 inch, Wi-Fi)" + case (7, 4): + return "iPad Pro (10.5 inch, Wi-Fi+LTE)" + case (7 ,5): + return "6th Gen iPad (WiFi)" + case (7, 6): + return "6th Gen iPad (Cellular)" + case (7 ,11): + return "7th Gen iPad (10.2 inch, WiFi)" + case (7, 12): + return "7th Gen iPad (10.2 inch, Cellular)" + case (8, 1): + return "iPad Pro (11 inch, Wi-Fi)" + case (8, 2): + return "iPad Pro (11 inch, Wi-Fi, 1TB)" + case (8, 3): + return "iPad Pro (11 inch, Wi-Fi+LTE)" + case (8, 4): + return "iPad Pro (11 inch, Wi-Fi+LTE, 1TB)" + case (8, 5): + return "3rd Gen iPad Pro (12.9 inch, Wi-Fi)" + case (8, 6): + return "3rd Gen iPad Pro (12.9 inch, Wi-Fi, 1TB)" + case (8, 7): + return "3rd Gen iPad Pro (12.9 inch, Wi-Fi+LTE)" + case (8, 8): + return "3rd Gen iPad Pro (12.9 inch, Wi-Fi+LTE, 1TB)" + case (8, 9): + return "2nd Gen iPad Pro (11 inch, Wi-Fi)" + case (8, 10): + return "2nd Gen iPad Pro (11 inch, Wi-Fi+LTE)" + case (8, 11): + return "4th Gen iPad Pro (12.9 inch, Wi-Fi)" + case (8, 12): + return "4th Gen iPad Pro (12.9 inch, Wi-Fi+LTE)" + case (11, 1): + return "5th Gen iPad Mini (Wi-Fi)" + case (11, 2): + return "5th Gen iPad Mini (Wi-Fi+LTE)" + case (11, 3): + return "3rd Gen iPad Air (Wi-Fi)" + case (11, 4): + return "3rd Gen iPad Air (Wi-Fi+LTE)" + case (13, 1): + return "4th Gen iPad Air (Wi-Fi)" + case (13, 16): + return "5th Gen iPad Air (Wi-Fi)" + case (13, 17): + return "5th Gen iPad Air (Wi-Fi+5G)" + case (13, 2): + return "4th Gen iPad Air (Wi-Fi+LTE)" + case (11, 6): + return "8th Gen iPad (10.2 inch, WiFi)" + case (11, 7): + return "8th Gen iPad (10.2 inch, Cellular)" + case (13, 4): + return "3rd Gen iPad Pro (11 inch, Wi-Fi)" + case (13, 5): + return "3rd Gen iPad Pro (11 inch, Wi-Fi, 16GB RAM)" + case (13, 6): + return "3rd Gen iPad Pro (11 inch, Wi-Fi+5G)" + case (13, 7): + return "3rd Gen iPad Pro (11 inch, Wi-Fi+5G, 16GB RAM)" + case (14, 3): + return "4rd Gen iPad Pro (11 inch, Wi-Fi)" + case (14, 4): + return "4rd Gen iPad Pro (11 inch, Wi-Fi+5G)" + case (13, 8): + return "5th Gen iPad Pro (12.9 inch, Wi-Fi)" + case (13, 9): + return "5th Gen iPad Pro (12.9 inch, Wi-Fi, 16GB RAM)" + case (13, 10): + return "5th Gen iPad Pro (12.9 inch, Wi-Fi+5G)" + case (13, 11): + return "5th Gen iPad Pro (12.9 inch, Wi-Fi+5G, 16GB RAM)" + case (14, 5): + return "6th Gen iPad Pro (12.9 inch, Wi-Fi)" + case (14, 6): + return "6th Gen iPad Pro (12.9 inch, Wi-Fi+5G)" + case (12, 1): + return "9th Gen iPad (10.2 inch, Wi-Fi)" + case (12, 2): + return "9th Gen iPad (10.2 inch, Wi-Fi+LTE)" + case (13, 18): + return "10th Gen iPad (10.9 inch, Wi-Fi)" + case (13, 19): + return "10th Gen iPad (10.9 inch, Wi-Fi+5G)" + case (14, 1): + return "6th Gen iPad mini (8.3 inch, Wi-Fi)" + case (14, 2): + return "6th Gen iPad mini (8.3 inch, Wi-Fi+5G)" + default: + return "unknown" + } + } +} diff --git a/Shared/UIDeviceComplete/LICENSE b/Shared/UIDeviceComplete/LICENSE new file mode 100644 index 00000000..2f3706b0 --- /dev/null +++ b/Shared/UIDeviceComplete/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2017 Nicholas Maccharoli + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/Shared/UIDeviceComplete/Screen.swift b/Shared/UIDeviceComplete/Screen.swift new file mode 100644 index 00000000..cfdcfe33 --- /dev/null +++ b/Shared/UIDeviceComplete/Screen.swift @@ -0,0 +1,93 @@ +// +// Screen.swift +// +// Copyright (c) 2017-2019 Nicholas Maccharoli +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +import UIKit + +public struct Screen { + init(width: Double, height: Double, scale: Double) { + self.width = width + self.height = height + self.scale = scale + } + + public let width: Double + public let height: Double + public let scale: Double + + public var adjustedScale: Double { + return 1.0 / scale + } +} + + +// MARK: - Detecting Screen size in Inches + +extension Screen { + public static var maxSupportedInches: Double { 12.9 } + + public var sizeInches: Double? { + switch (height, scale) { + case (480, _): return 3.5 + case (568, _): return 4.0 + case (667, 3.0), (736, _): return 5.5 + case (667, 1.0), (667, 2.0): return 4.7 + case (812, 3.0): return 5.8 + case (896, 2.0): return 6.1 + case (896, 3.0): return 6.5 + case (1024, _): return ipadSize1024() + case (1080, _): return 10.2 + case (1112, _): return 10.5 + case (1133, _): return 8.3 + case (1180, _): return 10.9 + case (1194, _): return 11.0 + case (1366, _): return 12.9 + default: return nil + } + } + + func ipadSize1024() -> Double { + let deviceModel = UIDevice().dc.deviceModel + switch deviceModel { + case .iPadMini, .iPadMini2, .iPadMini3, .iPadMini4, .iPadMini5: return 7.9 + case .iPadPro10_5Inch: return 10.5 + default: return 9.7 + } + } +} + +// MARK: - Detecting Screen aspect ratio + +extension Screen { + + public var aspectRatio: String? { + switch (height, scale) { + case (480, _): return "3:2" + case (568, _), (667, 3.0), (736, _), (667, 1.0), (667, 2.0): return "16:9" + case (812, 3.0), (896, 2.0), (896, 3.0): return "19.5:9" + case (1024, _), (1112, _), (1366, _), (1080, _): return "4:3" + // 3rd Gen iPad Pro aspect ratios still unknown + default: return nil + } + } + +} diff --git a/Shared/UIDeviceComplete/System.swift b/Shared/UIDeviceComplete/System.swift new file mode 100644 index 00000000..b8b9c190 --- /dev/null +++ b/Shared/UIDeviceComplete/System.swift @@ -0,0 +1,41 @@ +// +// System.swift +// +// Copyright (c) 2017-2019 Nicholas Maccharoli +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +import Foundation + +class System { + static var name: String? { + var systemInfo = utsname() + uname(&systemInfo) + let identifier = withUnsafePointer(to: &systemInfo.machine, { + $0.withMemoryRebound(to: CChar.self, capacity: Int(_SYS_NAMELEN)) { + ptr in String(cString: ptr) + } + }) + // Simulator Check + if identifier == "x86_64" || identifier == "i386" || identifier == "arm64" { + return ProcessInfo.processInfo.environment["SIMULATOR_MODEL_IDENTIFIER"] + } + return identifier + } +} diff --git a/Shared/UIDeviceComplete/UIDeviceComplete.swift b/Shared/UIDeviceComplete/UIDeviceComplete.swift new file mode 100644 index 00000000..c84baa7a --- /dev/null +++ b/Shared/UIDeviceComplete/UIDeviceComplete.swift @@ -0,0 +1,45 @@ +// +// UIDeviceComplete.swift +// +// Copyright (c) 2017-2019 Nicholas Maccharoli +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +import UIKit + +public final class UIDeviceComplete { + let base: Base + public init(_ base: Base) { + self.base = base + } +} + +public protocol UIDeviceCompleteCompatible { + associatedtype CompatibleType + + var dc: CompatibleType { get } +} + +public extension UIDeviceCompleteCompatible { + var dc: UIDeviceComplete { + return UIDeviceComplete(self) + } +} + +extension UIDevice: UIDeviceCompleteCompatible { } diff --git a/Shared/UIDeviceComplete/UIDeviceExtensions.swift b/Shared/UIDeviceComplete/UIDeviceExtensions.swift new file mode 100644 index 00000000..e95c2a5a --- /dev/null +++ b/Shared/UIDeviceComplete/UIDeviceExtensions.swift @@ -0,0 +1,77 @@ +// +// UIDevice+Extensions.swift +// +// Copyright (c) 2017-2019 Nicholas Maccharoli +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +import UIKit + +public extension UIDeviceComplete where Base == UIDevice { + + private var identifier: Identifier? { + return System.name.flatMap { + return Identifier($0) + } + } + + /// Device family i.e iPhone, iPod, iPad + var deviceFamily: DeviceFamily { + return identifier.flatMap { $0.type } ?? .unknown + } + + /// Specific model i.e iphone7 or iPhone7s + var deviceModel: DeviceModel { + return identifier.flatMap { DeviceModel(identifier: $0) } ?? .unknown + } + + /// Common name for device i.e "iPhone 7 Plus" + var commonDeviceName: String { + return identifier?.description ?? "unknown" + } + + /// Device family iPhone + var isIphone: Bool { + return deviceFamily == .iPhone + } + + /// Device family iPad + var isIpad: Bool { + return deviceFamily == .iPad + } + + /// Deivce family iPod + var isIpod: Bool { + return deviceFamily == .iPod + } + +} + + +// MARK: - Screen Size Detection + +public extension UIDeviceComplete where Base == UIDevice { + var screenSize: Screen { + let scale: Double = Double(UIScreen.main.scale) + let width: Double = Double(UIScreen.main.bounds.width) + let height: Double = Double(UIScreen.main.bounds.height) + + return Screen(width: width, height: height, scale: scale) + } +}