From 5a0eadd73fbb485c9be8fce94027b3b4666ec809 Mon Sep 17 00:00:00 2001 From: Vincent Neo <23420208+vincentneo@users.noreply.github.com> Date: Tue, 31 Jan 2023 00:25:00 +0800 Subject: [PATCH 1/5] watch devices support --- .../contents.xcworkspacedata | 7 + UIDeviceComplete.xcodeproj/project.pbxproj | 157 ++++++++++++++++++ WKDeviceComplete/DeviceModel.swift | 73 ++++++++ WKDeviceComplete/Identifier.swift | 152 +++++++++++++++++ WKDeviceComplete/Screen.swift | 60 +++++++ WKDeviceComplete/WKDeviceComplete.h | 19 +++ WKDeviceComplete/WKDeviceComplete.swift | 46 +++++ .../WKInterfaceDeviceExtensions.swift | 54 ++++++ 8 files changed, 568 insertions(+) create mode 100644 .swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata create mode 100644 WKDeviceComplete/DeviceModel.swift create mode 100644 WKDeviceComplete/Identifier.swift create mode 100644 WKDeviceComplete/Screen.swift create mode 100644 WKDeviceComplete/WKDeviceComplete.h create mode 100644 WKDeviceComplete/WKDeviceComplete.swift create mode 100644 WKDeviceComplete/WKInterfaceDeviceExtensions.swift diff --git a/.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata b/.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..919434a --- /dev/null +++ b/.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/UIDeviceComplete.xcodeproj/project.pbxproj b/UIDeviceComplete.xcodeproj/project.pbxproj index 009e021..03fb1fd 100644 --- a/UIDeviceComplete.xcodeproj/project.pbxproj +++ b/UIDeviceComplete.xcodeproj/project.pbxproj @@ -20,6 +20,13 @@ AA1C21AB1F385C2A0095BFBD /* UIDeviceExtensionsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA1C21A71F385C2A0095BFBD /* UIDeviceExtensionsTests.swift */; }; AAB1EF351F13866F003BBCF2 /* UIDeviceComplete.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = AAB1EF2B1F13866F003BBCF2 /* UIDeviceComplete.framework */; }; AAB1EF3C1F13866F003BBCF2 /* UIDeviceComplete.h in Headers */ = {isa = PBXBuildFile; fileRef = AAB1EF2E1F13866F003BBCF2 /* UIDeviceComplete.h */; settings = {ATTRIBUTES = (Public, ); }; }; + BF72D63129880FAC00B70A2B /* WKDeviceComplete.h in Headers */ = {isa = PBXBuildFile; fileRef = BF72D63029880FAC00B70A2B /* WKDeviceComplete.h */; settings = {ATTRIBUTES = (Public, ); }; }; + BF72D63629880FCF00B70A2B /* System.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA1C219A1F385BEC0095BFBD /* System.swift */; }; + BF72D638298810D700B70A2B /* DeviceModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF72D637298810D700B70A2B /* DeviceModel.swift */; }; + BF72D63A2988115000B70A2B /* Identifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF72D6392988115000B70A2B /* Identifier.swift */; }; + BF72D63C29881D1A00B70A2B /* Screen.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF72D63B29881D1A00B70A2B /* Screen.swift */; }; + BF72D63E2988206900B70A2B /* WKInterfaceDeviceExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF72D63D2988206900B70A2B /* WKInterfaceDeviceExtensions.swift */; }; + BF72D6402988209C00B70A2B /* WKDeviceComplete.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF72D63F2988209C00B70A2B /* WKDeviceComplete.swift */; }; BFA9121C215B9881000CD8A8 /* DeviceModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFA9121B215B9881000CD8A8 /* DeviceModelTests.swift */; }; /* End PBXBuildFile section */ @@ -50,6 +57,13 @@ AAB1EF2F1F13866F003BBCF2 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; AAB1EF341F13866F003BBCF2 /* UIDeviceCompleteTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = UIDeviceCompleteTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; AAB1EF3B1F13866F003BBCF2 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + BF72D62E29880FAC00B70A2B /* WKDeviceComplete.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = WKDeviceComplete.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + BF72D63029880FAC00B70A2B /* WKDeviceComplete.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = WKDeviceComplete.h; sourceTree = ""; }; + BF72D637298810D700B70A2B /* DeviceModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeviceModel.swift; sourceTree = ""; }; + BF72D6392988115000B70A2B /* Identifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Identifier.swift; sourceTree = ""; }; + BF72D63B29881D1A00B70A2B /* Screen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Screen.swift; sourceTree = ""; }; + BF72D63D2988206900B70A2B /* WKInterfaceDeviceExtensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WKInterfaceDeviceExtensions.swift; sourceTree = ""; }; + BF72D63F2988209C00B70A2B /* WKDeviceComplete.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WKDeviceComplete.swift; sourceTree = ""; }; BFA9121B215B9881000CD8A8 /* DeviceModelTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = DeviceModelTests.swift; path = Tests/DeviceModelTests.swift; sourceTree = SOURCE_ROOT; }; /* End PBXFileReference section */ @@ -69,6 +83,13 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + BF72D62B29880FAC00B70A2B /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ @@ -77,6 +98,7 @@ children = ( AAB1EF2D1F13866F003BBCF2 /* UIDeviceComplete */, AAB1EF381F13866F003BBCF2 /* UIDeviceCompleteTests */, + BF72D62F29880FAC00B70A2B /* WKDeviceComplete */, AAB1EF2C1F13866F003BBCF2 /* Products */, ); sourceTree = ""; @@ -86,6 +108,7 @@ children = ( AAB1EF2B1F13866F003BBCF2 /* UIDeviceComplete.framework */, AAB1EF341F13866F003BBCF2 /* UIDeviceCompleteTests.xctest */, + BF72D62E29880FAC00B70A2B /* WKDeviceComplete.framework */, ); name = Products; sourceTree = ""; @@ -127,6 +150,19 @@ name = Sources; sourceTree = ""; }; + BF72D62F29880FAC00B70A2B /* WKDeviceComplete */ = { + isa = PBXGroup; + children = ( + BF72D63029880FAC00B70A2B /* WKDeviceComplete.h */, + BF72D637298810D700B70A2B /* DeviceModel.swift */, + BF72D6392988115000B70A2B /* Identifier.swift */, + BF72D63B29881D1A00B70A2B /* Screen.swift */, + BF72D63F2988209C00B70A2B /* WKDeviceComplete.swift */, + BF72D63D2988206900B70A2B /* WKInterfaceDeviceExtensions.swift */, + ); + path = WKDeviceComplete; + sourceTree = ""; + }; /* End PBXGroup section */ /* Begin PBXHeadersBuildPhase section */ @@ -138,6 +174,14 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + BF72D62929880FAC00B70A2B /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + BF72D63129880FAC00B70A2B /* WKDeviceComplete.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXHeadersBuildPhase section */ /* Begin PBXNativeTarget section */ @@ -177,6 +221,24 @@ productReference = AAB1EF341F13866F003BBCF2 /* UIDeviceCompleteTests.xctest */; productType = "com.apple.product-type.bundle.unit-test"; }; + BF72D62D29880FAC00B70A2B /* WKDeviceComplete */ = { + isa = PBXNativeTarget; + buildConfigurationList = BF72D63429880FAC00B70A2B /* Build configuration list for PBXNativeTarget "WKDeviceComplete" */; + buildPhases = ( + BF72D62929880FAC00B70A2B /* Headers */, + BF72D62A29880FAC00B70A2B /* Sources */, + BF72D62B29880FAC00B70A2B /* Frameworks */, + BF72D62C29880FAC00B70A2B /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = WKDeviceComplete; + productName = WKDeviceComplete; + productReference = BF72D62E29880FAC00B70A2B /* WKDeviceComplete.framework */; + productType = "com.apple.product-type.framework"; + }; /* End PBXNativeTarget section */ /* Begin PBXProject section */ @@ -197,6 +259,10 @@ LastSwiftMigration = 1020; ProvisioningStyle = Automatic; }; + BF72D62D29880FAC00B70A2B = { + CreatedOnToolsVersion = 14.2; + ProvisioningStyle = Automatic; + }; }; }; buildConfigurationList = AAB1EF251F13866F003BBCF2 /* Build configuration list for PBXProject "UIDeviceComplete" */; @@ -214,6 +280,7 @@ targets = ( AAB1EF2A1F13866F003BBCF2 /* UIDeviceComplete */, AAB1EF331F13866F003BBCF2 /* UIDeviceCompleteTests */, + BF72D62D29880FAC00B70A2B /* WKDeviceComplete */, ); }; /* End PBXProject section */ @@ -233,6 +300,13 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + BF72D62C29880FAC00B70A2B /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXResourcesBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ @@ -262,6 +336,19 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + BF72D62A29880FAC00B70A2B /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + BF72D63E2988206900B70A2B /* WKInterfaceDeviceExtensions.swift in Sources */, + BF72D63629880FCF00B70A2B /* System.swift in Sources */, + BF72D63A2988115000B70A2B /* Identifier.swift in Sources */, + BF72D638298810D700B70A2B /* DeviceModel.swift in Sources */, + BF72D6402988209C00B70A2B /* WKDeviceComplete.swift in Sources */, + BF72D63C29881D1A00B70A2B /* Screen.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXSourcesBuildPhase section */ /* Begin PBXTargetDependency section */ @@ -475,6 +562,67 @@ }; name = Release; }; + BF72D63229880FAC00B70A2B /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + GCC_C_LANGUAGE_STANDARD = gnu11; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2023 Nicholas Maccharoli. All rights reserved."; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + MARKETING_VERSION = 1.0; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + PRODUCT_BUNDLE_IDENTIFIER = "com.vincent-neo.WKDeviceComplete"; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + SDKROOT = watchos; + SKIP_INSTALL = YES; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = 4; + WATCHOS_DEPLOYMENT_TARGET = 4.0; + }; + name = Debug; + }; + BF72D63329880FAC00B70A2B /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + GCC_C_LANGUAGE_STANDARD = gnu11; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2023 Nicholas Maccharoli. All rights reserved."; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + MARKETING_VERSION = 1.0; + MTL_FAST_MATH = YES; + PRODUCT_BUNDLE_IDENTIFIER = "com.vincent-neo.WKDeviceComplete"; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + SDKROOT = watchos; + SKIP_INSTALL = YES; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = 4; + WATCHOS_DEPLOYMENT_TARGET = 4.0; + }; + name = Release; + }; /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ @@ -505,6 +653,15 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; + BF72D63429880FAC00B70A2B /* Build configuration list for PBXNativeTarget "WKDeviceComplete" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + BF72D63229880FAC00B70A2B /* Debug */, + BF72D63329880FAC00B70A2B /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; /* End XCConfigurationList section */ }; rootObject = AAB1EF221F13866F003BBCF2 /* Project object */; diff --git a/WKDeviceComplete/DeviceModel.swift b/WKDeviceComplete/DeviceModel.swift new file mode 100644 index 0000000..82500b9 --- /dev/null +++ b/WKDeviceComplete/DeviceModel.swift @@ -0,0 +1,73 @@ +// +// DeviceModel.swift +// +// Copyright (c) 2017-2023 Nicholas Maccharoli +// Copyright (c) 2023 Vincent Neo +// +// 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 firstGen + case series1 + case series2 + case series3 + case series4 + case series5 + case se + case series6 + case series7 + case series8 + case se2 + case ultra + + case unknown +} + +// MARK: - init + +extension DeviceModel { + init(identifier: Identifier) { + self = DeviceModel.detectWatchModel(with: identifier) + } +} + +extension DeviceModel { + fileprivate static func detectWatchModel(with identifier: Identifier) -> DeviceModel { + guard let major = identifier.version.major, + let minor = identifier.version.minor + else { return .unknown } + + switch (major, minor) { + case (1, _): return .firstGen + case (2, 3), (2, 4): return .series2 + case (2, 6), (2, 7): return .series1 + case (3, _): return .series3 + case (4, _): return .series4 + case (5, 1), (5, 2), (5, 3), (5, 4): return .series5 + case (5, 9), (5, 10), (5, 11), (5, 12): return .se + case (6, 1), (6, 2), (6, 3), (6, 4): return .series6 + case (6, 6), (6, 7), (6, 8), (6, 9): return .series7 + case (6, 10), (6, 11), (6, 12), (6, 13): return .se2 + case (6, 14), (6, 15), (6, 16), (6, 17): return .series8 + case (6, 18): return .ultra + + default: return .unknown + } + } +} diff --git a/WKDeviceComplete/Identifier.swift b/WKDeviceComplete/Identifier.swift new file mode 100644 index 0000000..341f961 --- /dev/null +++ b/WKDeviceComplete/Identifier.swift @@ -0,0 +1,152 @@ +// +// Identifier.swift +// +// Copyright (c) 2017-2023 Nicholas Maccharoli +// Copyright (c) 2023 Vincent Neo +// +// 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 version: (major: Int?, minor: Int?) + + init(_ identifier: String) { + let (major, minor) = Identifier.typeVersionComponents(with: identifier) + self.version = (major, minor) + } +} + + +// MARK: - Identifier String parsing + +extension Identifier { + static func typeVersionComponents(with identifierString: 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 (major, minor) + } +} + + +// MARK: - String Representation - Watch + +extension Identifier: CustomStringConvertible { + var description: String { + + guard let major = version.major, + let minor = version.minor + else { return "unknown" } + + return watchStringRepresentable(major: major, minor: minor) + } + + private func watchStringRepresentable(major: Int, minor: Int) -> String { + switch (major, minor) { + case (1, 1): + return "Apple Watch (1st generation), 38mm case" + case (1, 2): + return "Apple Watch (1st generation), 42mm case" + case (2, 3): + return "Apple Watch Series 2, 38mm case" + case (2, 4): + return "Apple Watch Series 2, 42mm case" + case (2, 6): + return "Apple Watch Series 1, 38mm case" + case (2, 7): + return "Apple Watch Series 1, 42mm case" + case (3, 1): + return "Apple Watch Series 3, 38mm case (GPS + Cellular)" + case (3, 2): + return "Apple Watch Series 3, 42mm case (GPS + Cellular)" + case (3, 3): + return "Apple Watch Series 3, 38mm case (GPS)" + case (3, 4): + return "Apple Watch Series 3, 42mm case (GPS)" + case (4, 1): + return "Apple Watch Series 4, 40mm case (GPS)" + case (4, 2): + return "Apple Watch Series 4, 44mm case (GPS)" + case (4, 3): + return "Apple Watch Series 4, 40mm case (GPS + Cellular)" + case (4, 4): + return "Apple Watch Series 4, 44mm case (GPS + Cellular)" + case (5, 1): + return "Apple Watch Series 5, 40mm case (GPS)" + case (5, 2): + return "Apple Watch Series 5, 44mm case (GPS)" + case (5, 3): + return "Apple Watch Series 5, 40mm case (GPS + Cellular)" + case (5, 4): + return "Apple Watch Series 5, 44mm case (GPS + Cellular)" + case (5, 9): + return "Apple Watch SE, 40mm case (GPS)" + case (5, 10): + return "Apple Watch SE, 44mm case (GPS)" + case (5, 11): + return "Apple Watch SE, 40mm case (GPS + Cellular)" + case (5, 12): + return "Apple Watch SE, 44mm case (GPS + Cellular)" + case (6, 1): + return "Apple Watch Series 6, 40mm case (GPS)" + case (6, 2): + return "Apple Watch Series 6, 44mm case (GPS)" + case (6, 3): + return "Apple Watch Series 6, 40mm case (GPS + Cellular)" + case (6, 4): + return "Apple Watch Series 6, 44mm case (GPS + Cellular)" + case (6, 6): + return "Apple Watch Series 7, 41mm case (GPS)" + case (6, 7): + return "Apple Watch Series 7, 45mm case (GPS)" + case (6, 8): + return "Apple Watch Series 7, 41mm case (GPS + Cellular)" + case (6, 9): + return "Apple Watch Series 7, 45mm case (GPS + Cellular)" + case (6, 10): + return "Apple Watch SE (2nd Generation), 40mm case (GPS)" + case (6, 11): + return "Apple Watch SE (2nd Generation), 44mm case (GPS)" + case (6, 12): + return "Apple Watch SE (2nd Generation), 40mm case (GPS + Cellular)" + case (6, 13): + return "Apple Watch SE (2nd Generation), 44mm case (GPS + Cellular)" + case (6, 14): + return "Apple Watch Series 8, 41mm case (GPS)" + case (6, 15): + return "Apple Watch Series 8, 45mm case (GPS)" + case (6, 16): + return "Apple Watch Series 8, 41mm case (GPS + Cellular)" + case (6, 17): + return "Apple Watch Series 8, 45mm case (GPS + Cellular)" + case (6, 18): + return "Apple Watch Ultra" + default: + return "unknown" + } + } + +} diff --git a/WKDeviceComplete/Screen.swift b/WKDeviceComplete/Screen.swift new file mode 100644 index 0000000..1634b48 --- /dev/null +++ b/WKDeviceComplete/Screen.swift @@ -0,0 +1,60 @@ +// +// Screen.swift +// +// Copyright (c) 2017-2023 Nicholas Maccharoli +// Copyright (c) 2023 Vincent Neo +// +// 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 struct Screen { + let caseSize: Int? +} + +extension Screen { + init(identifier: Identifier) { + self.caseSize = Screen.detectCaseSize(with: identifier) + } +} + +// MARK: - Detecting Screen size by model, in mm + +extension Screen { + fileprivate static func detectCaseSize(with identifier: Identifier) -> Int? { + guard let major = identifier.version.major, + let minor = identifier.version.minor + else { return nil } + + switch (major, minor) { + case (1, 1), (2, 3), (2, 6), (3, 1), (3, 3): return 38 + case (1, 2), (2, 4), (2, 7), (3, 2), (3, 4): return 42 + + case (4, 1), (4, 3), (5, 1), (5, 3), (5, 9), + (5, 11), (6, 1), (6, 3), (6, 10), (6, 12): return 40 + case (4, 2), (4, 4), (5, 2), (5, 4), (5, 10), + (5, 12), (6, 2), (6, 4), (6, 11), (6, 13): return 44 + + case (6, 6), (6, 8), (6, 14), (6, 16): return 41 + case (6, 7), (6, 9), (6, 15), (6, 17): return 45 + + case (6, 18): return 49 + + default: return nil + } + } +} diff --git a/WKDeviceComplete/WKDeviceComplete.h b/WKDeviceComplete/WKDeviceComplete.h new file mode 100644 index 0000000..6b75725 --- /dev/null +++ b/WKDeviceComplete/WKDeviceComplete.h @@ -0,0 +1,19 @@ +// +// WKDeviceComplete.h +// WKDeviceComplete +// +// Created by Vincent Neo on 30/1/23. +// Copyright © 2023 Nicholas Maccharoli. All rights reserved. +// + +#import + +//! Project version number for WKDeviceComplete. +FOUNDATION_EXPORT double WKDeviceCompleteVersionNumber; + +//! Project version string for WKDeviceComplete. +FOUNDATION_EXPORT const unsigned char WKDeviceCompleteVersionString[]; + +// In this header, you should import all the public headers of your framework using statements like #import + + diff --git a/WKDeviceComplete/WKDeviceComplete.swift b/WKDeviceComplete/WKDeviceComplete.swift new file mode 100644 index 0000000..a4312be --- /dev/null +++ b/WKDeviceComplete/WKDeviceComplete.swift @@ -0,0 +1,46 @@ +// +// WKDeviceComplete.swift +// +// Copyright (c) 2017-2023 Nicholas Maccharoli +// Copyright (c) 2023 Vincent Neo +// +// 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 WatchKit + +public final class WKDeviceComplete { + let base: Base + public init(_ base: Base) { + self.base = base + } +} + +public protocol WKDeviceCompleteCompatible { + associatedtype CompatibleType + + var dc: CompatibleType { get } +} + +public extension WKDeviceCompleteCompatible { + var dc: WKDeviceComplete { + return WKDeviceComplete(self) + } +} + +extension WKInterfaceDevice: WKDeviceCompleteCompatible { } diff --git a/WKDeviceComplete/WKInterfaceDeviceExtensions.swift b/WKDeviceComplete/WKInterfaceDeviceExtensions.swift new file mode 100644 index 0000000..cf19473 --- /dev/null +++ b/WKDeviceComplete/WKInterfaceDeviceExtensions.swift @@ -0,0 +1,54 @@ +// +// WKInterfaceDeviceExtensions.swift +// +// Copyright (c) 2017-2023 Nicholas Maccharoli +// Copyright (c) 2023 Vincent Neo +// +// 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 WatchKit + +public extension WKDeviceComplete where Base == WKInterfaceDevice { + + private var identifier: Identifier? { + return System.name.flatMap { + return Identifier($0) + } + } + + /// 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" + } + +} + +// MARK: - Screen Size Detection + +public extension WKDeviceComplete where Base == WKInterfaceDevice { + var screenSize: Screen? { + guard let identifier else { return nil } + return Screen(identifier: identifier) + } +} From f9d8a7bab19b66315c2997c1e932baf165bf94c4 Mon Sep 17 00:00:00 2001 From: Vincent Neo <23420208+vincentneo@users.noreply.github.com> Date: Thu, 14 Sep 2023 12:07:57 +0800 Subject: [PATCH 2/5] Support for Series 9 and Ultra 2 watches --- WKDeviceComplete/DeviceModel.swift | 4 ++++ WKDeviceComplete/Identifier.swift | 10 ++++++++++ WKDeviceComplete/Screen.swift | 8 +++++--- 3 files changed, 19 insertions(+), 3 deletions(-) diff --git a/WKDeviceComplete/DeviceModel.swift b/WKDeviceComplete/DeviceModel.swift index 82500b9..3d80f1a 100644 --- a/WKDeviceComplete/DeviceModel.swift +++ b/WKDeviceComplete/DeviceModel.swift @@ -35,6 +35,8 @@ public enum DeviceModel: CaseIterable { case series8 case se2 case ultra + case series9 + case ultra2 case unknown } @@ -66,6 +68,8 @@ extension DeviceModel { case (6, 10), (6, 11), (6, 12), (6, 13): return .se2 case (6, 14), (6, 15), (6, 16), (6, 17): return .series8 case (6, 18): return .ultra + case (7, 1), (7, 2), (7, 3), (7, 4): return .series9 + case (7, 5): return .ultra2 default: return .unknown } diff --git a/WKDeviceComplete/Identifier.swift b/WKDeviceComplete/Identifier.swift index 341f961..b282b74 100644 --- a/WKDeviceComplete/Identifier.swift +++ b/WKDeviceComplete/Identifier.swift @@ -144,6 +144,16 @@ extension Identifier: CustomStringConvertible { return "Apple Watch Series 8, 45mm case (GPS + Cellular)" case (6, 18): return "Apple Watch Ultra" + case (7, 1): + return "Apple Watch Series 9, 41mm case (GPS)" + case (7, 2): + return "Apple Watch Series 9, 45mm case (GPS)" + case (7, 3): + return "Apple Watch Series 9, 41mm case (GPS + Cellular)" + case (7, 4): + return "Apple Watch Series 9, 45mm case (GPS + Cellular)" + case (7, 5): + return "Apple Watch Ultra 2" default: return "unknown" } diff --git a/WKDeviceComplete/Screen.swift b/WKDeviceComplete/Screen.swift index 1634b48..c5856de 100644 --- a/WKDeviceComplete/Screen.swift +++ b/WKDeviceComplete/Screen.swift @@ -49,10 +49,12 @@ extension Screen { case (4, 2), (4, 4), (5, 2), (5, 4), (5, 10), (5, 12), (6, 2), (6, 4), (6, 11), (6, 13): return 44 - case (6, 6), (6, 8), (6, 14), (6, 16): return 41 - case (6, 7), (6, 9), (6, 15), (6, 17): return 45 + case (6, 6), (6, 8), (6, 14), (6, 16), + (7, 1), (7, 3): return 41 + case (6, 7), (6, 9), (6, 15), (6, 17), + (7, 2), (7, 4): return 45 - case (6, 18): return 49 + case (6, 18), (7, 5): return 49 default: return nil } From 8c0cbe4d5c2adc8df4adbc8052feea88abdcc701 Mon Sep 17 00:00:00 2001 From: Vincent Neo <23420208+vincentneo@users.noreply.github.com> Date: Sat, 16 Sep 2023 16:56:19 +0800 Subject: [PATCH 3/5] merge ios and watchos codebase into one --- Package.swift | 2 +- Sources/DeviceFamily.swift | 3 + Sources/DeviceModel.swift | 66 ++++++- Sources/Identifier.swift | 97 +++++++++++ Sources/Screen.swift | 42 ++++- Sources/UIDeviceComplete.swift | 8 + Sources/UIDeviceExtensions.swift | 19 +- UIDeviceComplete.xcodeproj/project.pbxproj | 44 ++--- WKDeviceComplete/DeviceModel.swift | 77 --------- WKDeviceComplete/Identifier.swift | 162 ------------------ WKDeviceComplete/Screen.swift | 62 ------- WKDeviceComplete/WKDeviceComplete.h | 19 -- WKDeviceComplete/WKDeviceComplete.swift | 46 ----- .../WKInterfaceDeviceExtensions.swift | 54 ------ 14 files changed, 238 insertions(+), 463 deletions(-) delete mode 100644 WKDeviceComplete/DeviceModel.swift delete mode 100644 WKDeviceComplete/Identifier.swift delete mode 100644 WKDeviceComplete/Screen.swift delete mode 100644 WKDeviceComplete/WKDeviceComplete.h delete mode 100644 WKDeviceComplete/WKDeviceComplete.swift delete mode 100644 WKDeviceComplete/WKInterfaceDeviceExtensions.swift diff --git a/Package.swift b/Package.swift index 3ac5ed8..4e31a0b 100644 --- a/Package.swift +++ b/Package.swift @@ -25,7 +25,7 @@ import PackageDescription let package = Package( name: "UIDeviceComplete", platforms: [ - .iOS(.v11) + .iOS(.v11), .watchOS(.v4) ], products: [ .library(name: "UIDeviceComplete", targets: ["UIDeviceComplete"]) diff --git a/Sources/DeviceFamily.swift b/Sources/DeviceFamily.swift index 545ede2..1e353ec 100644 --- a/Sources/DeviceFamily.swift +++ b/Sources/DeviceFamily.swift @@ -26,6 +26,7 @@ public enum DeviceFamily: String { case iPhone case iPod case iPad + case watch case unknown public init(rawValue: String) { @@ -36,6 +37,8 @@ public enum DeviceFamily: String { self = .iPod case "iPad": self = .iPad + case "Watch": + self = .watch default: self = .unknown } diff --git a/Sources/DeviceModel.swift b/Sources/DeviceModel.swift index c9b5682..96ca16b 100644 --- a/Sources/DeviceModel.swift +++ b/Sources/DeviceModel.swift @@ -23,6 +23,7 @@ public enum DeviceModel: CaseIterable { + #if os(iOS) case iPhone4, iPhone4S case iPhone5, iPhone5C, iPhone5S case iPhone6, iPhone6Plus @@ -62,7 +63,24 @@ public enum DeviceModel: CaseIterable { case iPodTouchFirstGen, iPodTouchSecondGen, iPodTouchThirdGen, iPodTouchFourthGen, iPodTouchFifthGen, iPodTouchSixthGen, iPodTouchSeventhGen - + + #elseif os(watchOS) + case firstGen + case series1 + case series2 + case series3 + case series4 + case series5 + case se + case series6 + case series7 + case series8 + case se2 + case ultra + case series9 + case ultra2 + #endif + case unknown } @@ -72,19 +90,24 @@ public enum DeviceModel: CaseIterable { extension DeviceModel { init(identifier: Identifier) { switch identifier.type { + #if os(iOS) case .iPhone: self = DeviceModel.detectIphoneModel(with: identifier) case .iPad: self = DeviceModel.detectIpadModel(with: identifier) case .iPod: self = DeviceModel.detectIpodModel(with: identifier) + #elseif os(watchOS) + case .watch: + self = DeviceModel.detectWatchModel(with: identifier) + #endif default: self = .unknown } } } - +#if os(iOS) // MARK: Detecting iPhone Models extension DeviceModel { @@ -154,8 +177,9 @@ extension DeviceModel { } } } +#endif - +#if os(iOS) // MARK: Detecting iPad Models extension DeviceModel { @@ -207,8 +231,9 @@ extension DeviceModel { } } } +#endif - +#if os(iOS) // MARK: Detecting iPod Models extension DeviceModel { @@ -230,8 +255,40 @@ extension DeviceModel { } } } +#endif +#if os(watchOS) +// MARK: Detecting Apple Watch Models + +extension DeviceModel { + fileprivate static func detectWatchModel(with identifier: Identifier) -> DeviceModel { + guard let major = identifier.version.major, + let minor = identifier.version.minor + else { return .unknown } + + switch (major, minor) { + case (1, _): return .firstGen + case (2, 3), (2, 4): return .series2 + case (2, 6), (2, 7): return .series1 + case (3, _): return .series3 + case (4, _): return .series4 + case (5, 1), (5, 2), (5, 3), (5, 4): return .series5 + case (5, 9), (5, 10), (5, 11), (5, 12): return .se + case (6, 1), (6, 2), (6, 3), (6, 4): return .series6 + case (6, 6), (6, 7), (6, 8), (6, 9): return .series7 + case (6, 10), (6, 11), (6, 12), (6, 13): return .se2 + case (6, 14), (6, 15), (6, 16), (6, 17): return .series8 + case (6, 18): return .ultra + case (7, 1), (7, 2), (7, 3), (7, 4): return .series9 + case (7, 5): return .ultra2 + + default: return .unknown + } + } +} +#endif +#if os(iOS) // MARK: Detecting the Notch extension DeviceModel { @@ -266,3 +323,4 @@ extension DeviceModel { } } } +#endif diff --git a/Sources/Identifier.swift b/Sources/Identifier.swift index ff73c67..20e5f84 100644 --- a/Sources/Identifier.swift +++ b/Sources/Identifier.swift @@ -69,6 +69,8 @@ extension Identifier: CustomStringConvertible { return iPadStringRepresentation(major: major, minor: minor) case .iPod: return iPodStringRepresentation(major: major, minor: minor) + case .watch: + return watchStringRepresentable(major: major, minor: minor) case .unknown: return "unknown" } @@ -384,4 +386,99 @@ extension Identifier: CustomStringConvertible { return "unknown" } } + + private func watchStringRepresentable(major: Int, minor: Int) -> String { + switch (major, minor) { + case (1, 1): + return "Apple Watch (1st generation), 38mm case" + case (1, 2): + return "Apple Watch (1st generation), 42mm case" + case (2, 3): + return "Apple Watch Series 2, 38mm case" + case (2, 4): + return "Apple Watch Series 2, 42mm case" + case (2, 6): + return "Apple Watch Series 1, 38mm case" + case (2, 7): + return "Apple Watch Series 1, 42mm case" + case (3, 1): + return "Apple Watch Series 3, 38mm case (GPS + Cellular)" + case (3, 2): + return "Apple Watch Series 3, 42mm case (GPS + Cellular)" + case (3, 3): + return "Apple Watch Series 3, 38mm case (GPS)" + case (3, 4): + return "Apple Watch Series 3, 42mm case (GPS)" + case (4, 1): + return "Apple Watch Series 4, 40mm case (GPS)" + case (4, 2): + return "Apple Watch Series 4, 44mm case (GPS)" + case (4, 3): + return "Apple Watch Series 4, 40mm case (GPS + Cellular)" + case (4, 4): + return "Apple Watch Series 4, 44mm case (GPS + Cellular)" + case (5, 1): + return "Apple Watch Series 5, 40mm case (GPS)" + case (5, 2): + return "Apple Watch Series 5, 44mm case (GPS)" + case (5, 3): + return "Apple Watch Series 5, 40mm case (GPS + Cellular)" + case (5, 4): + return "Apple Watch Series 5, 44mm case (GPS + Cellular)" + case (5, 9): + return "Apple Watch SE, 40mm case (GPS)" + case (5, 10): + return "Apple Watch SE, 44mm case (GPS)" + case (5, 11): + return "Apple Watch SE, 40mm case (GPS + Cellular)" + case (5, 12): + return "Apple Watch SE, 44mm case (GPS + Cellular)" + case (6, 1): + return "Apple Watch Series 6, 40mm case (GPS)" + case (6, 2): + return "Apple Watch Series 6, 44mm case (GPS)" + case (6, 3): + return "Apple Watch Series 6, 40mm case (GPS + Cellular)" + case (6, 4): + return "Apple Watch Series 6, 44mm case (GPS + Cellular)" + case (6, 6): + return "Apple Watch Series 7, 41mm case (GPS)" + case (6, 7): + return "Apple Watch Series 7, 45mm case (GPS)" + case (6, 8): + return "Apple Watch Series 7, 41mm case (GPS + Cellular)" + case (6, 9): + return "Apple Watch Series 7, 45mm case (GPS + Cellular)" + case (6, 10): + return "Apple Watch SE (2nd Generation), 40mm case (GPS)" + case (6, 11): + return "Apple Watch SE (2nd Generation), 44mm case (GPS)" + case (6, 12): + return "Apple Watch SE (2nd Generation), 40mm case (GPS + Cellular)" + case (6, 13): + return "Apple Watch SE (2nd Generation), 44mm case (GPS + Cellular)" + case (6, 14): + return "Apple Watch Series 8, 41mm case (GPS)" + case (6, 15): + return "Apple Watch Series 8, 45mm case (GPS)" + case (6, 16): + return "Apple Watch Series 8, 41mm case (GPS + Cellular)" + case (6, 17): + return "Apple Watch Series 8, 45mm case (GPS + Cellular)" + case (6, 18): + return "Apple Watch Ultra" + case (7, 1): + return "Apple Watch Series 9, 41mm case (GPS)" + case (7, 2): + return "Apple Watch Series 9, 45mm case (GPS)" + case (7, 3): + return "Apple Watch Series 9, 41mm case (GPS + Cellular)" + case (7, 4): + return "Apple Watch Series 9, 45mm case (GPS + Cellular)" + case (7, 5): + return "Apple Watch Ultra 2" + default: + return "unknown" + } + } } diff --git a/Sources/Screen.swift b/Sources/Screen.swift index 5534a67..f6b9175 100644 --- a/Sources/Screen.swift +++ b/Sources/Screen.swift @@ -21,9 +21,12 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. +#if os(iOS) import UIKit +#endif public struct Screen { + #if os(iOS) init(width: Double, height: Double, scale: Double) { self.width = width self.height = height @@ -37,9 +40,16 @@ public struct Screen { public var adjustedScale: Double { return 1.0 / scale } + #elseif os(watchOS) + init(identifier: Identifier) { + self.identifier = identifier + } + + let identifier: Identifier + #endif } - +#if os(iOS) // MARK: - Detecting Screen size in Inches extension Screen { @@ -89,3 +99,33 @@ extension Screen { } } +#endif + +#if os(watchOS) +extension Screen { + public var caseSize: Int? { + guard let major = identifier.version.major, + let minor = identifier.version.minor + else { return nil } + + switch (major, minor) { + case (1, 1), (2, 3), (2, 6), (3, 1), (3, 3): return 38 + case (1, 2), (2, 4), (2, 7), (3, 2), (3, 4): return 42 + + case (4, 1), (4, 3), (5, 1), (5, 3), (5, 9), + (5, 11), (6, 1), (6, 3), (6, 10), (6, 12): return 40 + case (4, 2), (4, 4), (5, 2), (5, 4), (5, 10), + (5, 12), (6, 2), (6, 4), (6, 11), (6, 13): return 44 + + case (6, 6), (6, 8), (6, 14), (6, 16), + (7, 1), (7, 3): return 41 + case (6, 7), (6, 9), (6, 15), (6, 17), + (7, 2), (7, 4): return 45 + + case (6, 18), (7, 5): return 49 + + default: return nil + } + } +} +#endif diff --git a/Sources/UIDeviceComplete.swift b/Sources/UIDeviceComplete.swift index c84baa7..f95503f 100644 --- a/Sources/UIDeviceComplete.swift +++ b/Sources/UIDeviceComplete.swift @@ -21,7 +21,11 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. +#if os(iOS) import UIKit +#elseif os(watchOS) +import WatchKit +#endif public final class UIDeviceComplete { let base: Base @@ -42,4 +46,8 @@ public extension UIDeviceCompleteCompatible { } } +#if os(iOS) extension UIDevice: UIDeviceCompleteCompatible { } +#elseif os(watchOS) +extension WKInterfaceDevice: UIDeviceCompleteCompatible { } +#endif diff --git a/Sources/UIDeviceExtensions.swift b/Sources/UIDeviceExtensions.swift index e95c2a5..6142ad7 100644 --- a/Sources/UIDeviceExtensions.swift +++ b/Sources/UIDeviceExtensions.swift @@ -21,9 +21,15 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. +#if os(iOS) import UIKit +public typealias DCDevice = UIDevice +#elseif os(watchOS) +import WatchKit +public typealias DCDevice = WKInterfaceDevice +#endif -public extension UIDeviceComplete where Base == UIDevice { +public extension UIDeviceComplete where Base == DCDevice { private var identifier: Identifier? { return System.name.flatMap { @@ -45,7 +51,8 @@ public extension UIDeviceComplete where Base == UIDevice { var commonDeviceName: String { return identifier?.description ?? "unknown" } - + + #if os(iOS) /// Device family iPhone var isIphone: Bool { return deviceFamily == .iPhone @@ -60,13 +67,14 @@ public extension UIDeviceComplete where Base == UIDevice { var isIpod: Bool { return deviceFamily == .iPod } - + #endif + } - +#if os(iOS) // MARK: - Screen Size Detection -public extension UIDeviceComplete where Base == UIDevice { +public extension UIDeviceComplete where Base == DCDevice { var screenSize: Screen { let scale: Double = Double(UIScreen.main.scale) let width: Double = Double(UIScreen.main.bounds.width) @@ -75,3 +83,4 @@ public extension UIDeviceComplete where Base == UIDevice { return Screen(width: width, height: height, scale: scale) } } +#endif diff --git a/UIDeviceComplete.xcodeproj/project.pbxproj b/UIDeviceComplete.xcodeproj/project.pbxproj index 03fb1fd..4b73f4f 100644 --- a/UIDeviceComplete.xcodeproj/project.pbxproj +++ b/UIDeviceComplete.xcodeproj/project.pbxproj @@ -20,14 +20,14 @@ AA1C21AB1F385C2A0095BFBD /* UIDeviceExtensionsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA1C21A71F385C2A0095BFBD /* UIDeviceExtensionsTests.swift */; }; AAB1EF351F13866F003BBCF2 /* UIDeviceComplete.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = AAB1EF2B1F13866F003BBCF2 /* UIDeviceComplete.framework */; }; AAB1EF3C1F13866F003BBCF2 /* UIDeviceComplete.h in Headers */ = {isa = PBXBuildFile; fileRef = AAB1EF2E1F13866F003BBCF2 /* UIDeviceComplete.h */; settings = {ATTRIBUTES = (Public, ); }; }; - BF72D63129880FAC00B70A2B /* WKDeviceComplete.h in Headers */ = {isa = PBXBuildFile; fileRef = BF72D63029880FAC00B70A2B /* WKDeviceComplete.h */; settings = {ATTRIBUTES = (Public, ); }; }; BF72D63629880FCF00B70A2B /* System.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA1C219A1F385BEC0095BFBD /* System.swift */; }; - BF72D638298810D700B70A2B /* DeviceModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF72D637298810D700B70A2B /* DeviceModel.swift */; }; - BF72D63A2988115000B70A2B /* Identifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF72D6392988115000B70A2B /* Identifier.swift */; }; - BF72D63C29881D1A00B70A2B /* Screen.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF72D63B29881D1A00B70A2B /* Screen.swift */; }; - BF72D63E2988206900B70A2B /* WKInterfaceDeviceExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF72D63D2988206900B70A2B /* WKInterfaceDeviceExtensions.swift */; }; - BF72D6402988209C00B70A2B /* WKDeviceComplete.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF72D63F2988209C00B70A2B /* WKDeviceComplete.swift */; }; BFA9121C215B9881000CD8A8 /* DeviceModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFA9121B215B9881000CD8A8 /* DeviceModelTests.swift */; }; + BFE460D02AB5A08700BE39DB /* Identifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA1C21981F385BEC0095BFBD /* Identifier.swift */; }; + BFE460D12AB5A08700BE39DB /* Screen.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA1C21991F385BEC0095BFBD /* Screen.swift */; }; + BFE460D22AB5A08700BE39DB /* DeviceFamily.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA1C21961F385BEC0095BFBD /* DeviceFamily.swift */; }; + BFE460D32AB5A08700BE39DB /* DeviceModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA1C21971F385BEC0095BFBD /* DeviceModel.swift */; }; + BFE460D42AB5A08C00BE39DB /* UIDeviceExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA1C219C1F385BEC0095BFBD /* UIDeviceExtensions.swift */; }; + BFE460D52AB5A08C00BE39DB /* UIDeviceComplete.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA1C219B1F385BEC0095BFBD /* UIDeviceComplete.swift */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -58,12 +58,6 @@ AAB1EF341F13866F003BBCF2 /* UIDeviceCompleteTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = UIDeviceCompleteTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; AAB1EF3B1F13866F003BBCF2 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; BF72D62E29880FAC00B70A2B /* WKDeviceComplete.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = WKDeviceComplete.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - BF72D63029880FAC00B70A2B /* WKDeviceComplete.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = WKDeviceComplete.h; sourceTree = ""; }; - BF72D637298810D700B70A2B /* DeviceModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeviceModel.swift; sourceTree = ""; }; - BF72D6392988115000B70A2B /* Identifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Identifier.swift; sourceTree = ""; }; - BF72D63B29881D1A00B70A2B /* Screen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Screen.swift; sourceTree = ""; }; - BF72D63D2988206900B70A2B /* WKInterfaceDeviceExtensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WKInterfaceDeviceExtensions.swift; sourceTree = ""; }; - BF72D63F2988209C00B70A2B /* WKDeviceComplete.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WKDeviceComplete.swift; sourceTree = ""; }; BFA9121B215B9881000CD8A8 /* DeviceModelTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = DeviceModelTests.swift; path = Tests/DeviceModelTests.swift; sourceTree = SOURCE_ROOT; }; /* End PBXFileReference section */ @@ -98,7 +92,6 @@ children = ( AAB1EF2D1F13866F003BBCF2 /* UIDeviceComplete */, AAB1EF381F13866F003BBCF2 /* UIDeviceCompleteTests */, - BF72D62F29880FAC00B70A2B /* WKDeviceComplete */, AAB1EF2C1F13866F003BBCF2 /* Products */, ); sourceTree = ""; @@ -150,19 +143,6 @@ name = Sources; sourceTree = ""; }; - BF72D62F29880FAC00B70A2B /* WKDeviceComplete */ = { - isa = PBXGroup; - children = ( - BF72D63029880FAC00B70A2B /* WKDeviceComplete.h */, - BF72D637298810D700B70A2B /* DeviceModel.swift */, - BF72D6392988115000B70A2B /* Identifier.swift */, - BF72D63B29881D1A00B70A2B /* Screen.swift */, - BF72D63F2988209C00B70A2B /* WKDeviceComplete.swift */, - BF72D63D2988206900B70A2B /* WKInterfaceDeviceExtensions.swift */, - ); - path = WKDeviceComplete; - sourceTree = ""; - }; /* End PBXGroup section */ /* Begin PBXHeadersBuildPhase section */ @@ -178,7 +158,6 @@ isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; files = ( - BF72D63129880FAC00B70A2B /* WKDeviceComplete.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -340,12 +319,13 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - BF72D63E2988206900B70A2B /* WKInterfaceDeviceExtensions.swift in Sources */, + BFE460D02AB5A08700BE39DB /* Identifier.swift in Sources */, + BFE460D52AB5A08C00BE39DB /* UIDeviceComplete.swift in Sources */, + BFE460D42AB5A08C00BE39DB /* UIDeviceExtensions.swift in Sources */, BF72D63629880FCF00B70A2B /* System.swift in Sources */, - BF72D63A2988115000B70A2B /* Identifier.swift in Sources */, - BF72D638298810D700B70A2B /* DeviceModel.swift in Sources */, - BF72D6402988209C00B70A2B /* WKDeviceComplete.swift in Sources */, - BF72D63C29881D1A00B70A2B /* Screen.swift in Sources */, + BFE460D32AB5A08700BE39DB /* DeviceModel.swift in Sources */, + BFE460D12AB5A08700BE39DB /* Screen.swift in Sources */, + BFE460D22AB5A08700BE39DB /* DeviceFamily.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/WKDeviceComplete/DeviceModel.swift b/WKDeviceComplete/DeviceModel.swift deleted file mode 100644 index 3d80f1a..0000000 --- a/WKDeviceComplete/DeviceModel.swift +++ /dev/null @@ -1,77 +0,0 @@ -// -// DeviceModel.swift -// -// Copyright (c) 2017-2023 Nicholas Maccharoli -// Copyright (c) 2023 Vincent Neo -// -// 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 firstGen - case series1 - case series2 - case series3 - case series4 - case series5 - case se - case series6 - case series7 - case series8 - case se2 - case ultra - case series9 - case ultra2 - - case unknown -} - -// MARK: - init - -extension DeviceModel { - init(identifier: Identifier) { - self = DeviceModel.detectWatchModel(with: identifier) - } -} - -extension DeviceModel { - fileprivate static func detectWatchModel(with identifier: Identifier) -> DeviceModel { - guard let major = identifier.version.major, - let minor = identifier.version.minor - else { return .unknown } - - switch (major, minor) { - case (1, _): return .firstGen - case (2, 3), (2, 4): return .series2 - case (2, 6), (2, 7): return .series1 - case (3, _): return .series3 - case (4, _): return .series4 - case (5, 1), (5, 2), (5, 3), (5, 4): return .series5 - case (5, 9), (5, 10), (5, 11), (5, 12): return .se - case (6, 1), (6, 2), (6, 3), (6, 4): return .series6 - case (6, 6), (6, 7), (6, 8), (6, 9): return .series7 - case (6, 10), (6, 11), (6, 12), (6, 13): return .se2 - case (6, 14), (6, 15), (6, 16), (6, 17): return .series8 - case (6, 18): return .ultra - case (7, 1), (7, 2), (7, 3), (7, 4): return .series9 - case (7, 5): return .ultra2 - - default: return .unknown - } - } -} diff --git a/WKDeviceComplete/Identifier.swift b/WKDeviceComplete/Identifier.swift deleted file mode 100644 index b282b74..0000000 --- a/WKDeviceComplete/Identifier.swift +++ /dev/null @@ -1,162 +0,0 @@ -// -// Identifier.swift -// -// Copyright (c) 2017-2023 Nicholas Maccharoli -// Copyright (c) 2023 Vincent Neo -// -// 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 version: (major: Int?, minor: Int?) - - init(_ identifier: String) { - let (major, minor) = Identifier.typeVersionComponents(with: identifier) - self.version = (major, minor) - } -} - - -// MARK: - Identifier String parsing - -extension Identifier { - static func typeVersionComponents(with identifierString: 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 (major, minor) - } -} - - -// MARK: - String Representation - Watch - -extension Identifier: CustomStringConvertible { - var description: String { - - guard let major = version.major, - let minor = version.minor - else { return "unknown" } - - return watchStringRepresentable(major: major, minor: minor) - } - - private func watchStringRepresentable(major: Int, minor: Int) -> String { - switch (major, minor) { - case (1, 1): - return "Apple Watch (1st generation), 38mm case" - case (1, 2): - return "Apple Watch (1st generation), 42mm case" - case (2, 3): - return "Apple Watch Series 2, 38mm case" - case (2, 4): - return "Apple Watch Series 2, 42mm case" - case (2, 6): - return "Apple Watch Series 1, 38mm case" - case (2, 7): - return "Apple Watch Series 1, 42mm case" - case (3, 1): - return "Apple Watch Series 3, 38mm case (GPS + Cellular)" - case (3, 2): - return "Apple Watch Series 3, 42mm case (GPS + Cellular)" - case (3, 3): - return "Apple Watch Series 3, 38mm case (GPS)" - case (3, 4): - return "Apple Watch Series 3, 42mm case (GPS)" - case (4, 1): - return "Apple Watch Series 4, 40mm case (GPS)" - case (4, 2): - return "Apple Watch Series 4, 44mm case (GPS)" - case (4, 3): - return "Apple Watch Series 4, 40mm case (GPS + Cellular)" - case (4, 4): - return "Apple Watch Series 4, 44mm case (GPS + Cellular)" - case (5, 1): - return "Apple Watch Series 5, 40mm case (GPS)" - case (5, 2): - return "Apple Watch Series 5, 44mm case (GPS)" - case (5, 3): - return "Apple Watch Series 5, 40mm case (GPS + Cellular)" - case (5, 4): - return "Apple Watch Series 5, 44mm case (GPS + Cellular)" - case (5, 9): - return "Apple Watch SE, 40mm case (GPS)" - case (5, 10): - return "Apple Watch SE, 44mm case (GPS)" - case (5, 11): - return "Apple Watch SE, 40mm case (GPS + Cellular)" - case (5, 12): - return "Apple Watch SE, 44mm case (GPS + Cellular)" - case (6, 1): - return "Apple Watch Series 6, 40mm case (GPS)" - case (6, 2): - return "Apple Watch Series 6, 44mm case (GPS)" - case (6, 3): - return "Apple Watch Series 6, 40mm case (GPS + Cellular)" - case (6, 4): - return "Apple Watch Series 6, 44mm case (GPS + Cellular)" - case (6, 6): - return "Apple Watch Series 7, 41mm case (GPS)" - case (6, 7): - return "Apple Watch Series 7, 45mm case (GPS)" - case (6, 8): - return "Apple Watch Series 7, 41mm case (GPS + Cellular)" - case (6, 9): - return "Apple Watch Series 7, 45mm case (GPS + Cellular)" - case (6, 10): - return "Apple Watch SE (2nd Generation), 40mm case (GPS)" - case (6, 11): - return "Apple Watch SE (2nd Generation), 44mm case (GPS)" - case (6, 12): - return "Apple Watch SE (2nd Generation), 40mm case (GPS + Cellular)" - case (6, 13): - return "Apple Watch SE (2nd Generation), 44mm case (GPS + Cellular)" - case (6, 14): - return "Apple Watch Series 8, 41mm case (GPS)" - case (6, 15): - return "Apple Watch Series 8, 45mm case (GPS)" - case (6, 16): - return "Apple Watch Series 8, 41mm case (GPS + Cellular)" - case (6, 17): - return "Apple Watch Series 8, 45mm case (GPS + Cellular)" - case (6, 18): - return "Apple Watch Ultra" - case (7, 1): - return "Apple Watch Series 9, 41mm case (GPS)" - case (7, 2): - return "Apple Watch Series 9, 45mm case (GPS)" - case (7, 3): - return "Apple Watch Series 9, 41mm case (GPS + Cellular)" - case (7, 4): - return "Apple Watch Series 9, 45mm case (GPS + Cellular)" - case (7, 5): - return "Apple Watch Ultra 2" - default: - return "unknown" - } - } - -} diff --git a/WKDeviceComplete/Screen.swift b/WKDeviceComplete/Screen.swift deleted file mode 100644 index c5856de..0000000 --- a/WKDeviceComplete/Screen.swift +++ /dev/null @@ -1,62 +0,0 @@ -// -// Screen.swift -// -// Copyright (c) 2017-2023 Nicholas Maccharoli -// Copyright (c) 2023 Vincent Neo -// -// 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 struct Screen { - let caseSize: Int? -} - -extension Screen { - init(identifier: Identifier) { - self.caseSize = Screen.detectCaseSize(with: identifier) - } -} - -// MARK: - Detecting Screen size by model, in mm - -extension Screen { - fileprivate static func detectCaseSize(with identifier: Identifier) -> Int? { - guard let major = identifier.version.major, - let minor = identifier.version.minor - else { return nil } - - switch (major, minor) { - case (1, 1), (2, 3), (2, 6), (3, 1), (3, 3): return 38 - case (1, 2), (2, 4), (2, 7), (3, 2), (3, 4): return 42 - - case (4, 1), (4, 3), (5, 1), (5, 3), (5, 9), - (5, 11), (6, 1), (6, 3), (6, 10), (6, 12): return 40 - case (4, 2), (4, 4), (5, 2), (5, 4), (5, 10), - (5, 12), (6, 2), (6, 4), (6, 11), (6, 13): return 44 - - case (6, 6), (6, 8), (6, 14), (6, 16), - (7, 1), (7, 3): return 41 - case (6, 7), (6, 9), (6, 15), (6, 17), - (7, 2), (7, 4): return 45 - - case (6, 18), (7, 5): return 49 - - default: return nil - } - } -} diff --git a/WKDeviceComplete/WKDeviceComplete.h b/WKDeviceComplete/WKDeviceComplete.h deleted file mode 100644 index 6b75725..0000000 --- a/WKDeviceComplete/WKDeviceComplete.h +++ /dev/null @@ -1,19 +0,0 @@ -// -// WKDeviceComplete.h -// WKDeviceComplete -// -// Created by Vincent Neo on 30/1/23. -// Copyright © 2023 Nicholas Maccharoli. All rights reserved. -// - -#import - -//! Project version number for WKDeviceComplete. -FOUNDATION_EXPORT double WKDeviceCompleteVersionNumber; - -//! Project version string for WKDeviceComplete. -FOUNDATION_EXPORT const unsigned char WKDeviceCompleteVersionString[]; - -// In this header, you should import all the public headers of your framework using statements like #import - - diff --git a/WKDeviceComplete/WKDeviceComplete.swift b/WKDeviceComplete/WKDeviceComplete.swift deleted file mode 100644 index a4312be..0000000 --- a/WKDeviceComplete/WKDeviceComplete.swift +++ /dev/null @@ -1,46 +0,0 @@ -// -// WKDeviceComplete.swift -// -// Copyright (c) 2017-2023 Nicholas Maccharoli -// Copyright (c) 2023 Vincent Neo -// -// 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 WatchKit - -public final class WKDeviceComplete { - let base: Base - public init(_ base: Base) { - self.base = base - } -} - -public protocol WKDeviceCompleteCompatible { - associatedtype CompatibleType - - var dc: CompatibleType { get } -} - -public extension WKDeviceCompleteCompatible { - var dc: WKDeviceComplete { - return WKDeviceComplete(self) - } -} - -extension WKInterfaceDevice: WKDeviceCompleteCompatible { } diff --git a/WKDeviceComplete/WKInterfaceDeviceExtensions.swift b/WKDeviceComplete/WKInterfaceDeviceExtensions.swift deleted file mode 100644 index cf19473..0000000 --- a/WKDeviceComplete/WKInterfaceDeviceExtensions.swift +++ /dev/null @@ -1,54 +0,0 @@ -// -// WKInterfaceDeviceExtensions.swift -// -// Copyright (c) 2017-2023 Nicholas Maccharoli -// Copyright (c) 2023 Vincent Neo -// -// 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 WatchKit - -public extension WKDeviceComplete where Base == WKInterfaceDevice { - - private var identifier: Identifier? { - return System.name.flatMap { - return Identifier($0) - } - } - - /// 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" - } - -} - -// MARK: - Screen Size Detection - -public extension WKDeviceComplete where Base == WKInterfaceDevice { - var screenSize: Screen? { - guard let identifier else { return nil } - return Screen(identifier: identifier) - } -} From 8eb932898ef788ec11883ab1718c877e469d1bde Mon Sep 17 00:00:00 2001 From: Vincent Neo <23420208+vincentneo@users.noreply.github.com> Date: Thu, 21 Sep 2023 18:43:31 +0800 Subject: [PATCH 4/5] Add tests for watch devices --- Tests/DeviceFamilyTests.swift | 7 +- Tests/DeviceModelTests.swift | 137 ++++++++++++++++ Tests/IdentifierTests.swift | 182 ++++++++++++++++++++- Tests/UIDeviceExtensionsTests.swift | 8 + UIDeviceComplete.xcodeproj/project.pbxproj | 151 ++--------------- 5 files changed, 345 insertions(+), 140 deletions(-) diff --git a/Tests/DeviceFamilyTests.swift b/Tests/DeviceFamilyTests.swift index 8a83d79..73ffd38 100644 --- a/Tests/DeviceFamilyTests.swift +++ b/Tests/DeviceFamilyTests.swift @@ -41,6 +41,11 @@ class DeviceFamilyTests: XCTestCase { XCTAssert(deviceFamily == .iPad, "DeviceFamily - .iPad is failing") } + func testDeviceFamilyWatch() { + let deviceFamily = DeviceFamily(rawValue: "Watch") + XCTAssert(deviceFamily == .watch, "DeviceFamily - .watch is failing") + } + func testInvalidDeviceFamily() { let deviceFamily = DeviceFamily(rawValue: "Apple II") XCTAssert(deviceFamily == .unknown, "DeviceFamily - .unknown is failing") @@ -54,5 +59,5 @@ class DeviceFamilyTests: XCTestCase { XCTAssert(!(deviceFamily.isSimulator), "DeviceFamily - .isSimulator is failing") #endif } - + } diff --git a/Tests/DeviceModelTests.swift b/Tests/DeviceModelTests.swift index 8c8d7d8..020e47c 100644 --- a/Tests/DeviceModelTests.swift +++ b/Tests/DeviceModelTests.swift @@ -28,6 +28,7 @@ import XCTest class DeviceModelTests: XCTestCase { + #if os(iOS) // MARK: - iPhone Device Model tests func testDeviceModelIPhone4() { @@ -511,4 +512,140 @@ class DeviceModelTests: XCTestCase { withModels.forEach { XCTAssertTrue($0.hasDynamicIsland) } withoutModels.forEach { XCTAssertFalse($0.hasDynamicIsland) } } + #endif + + #if os(watchOS) + // MARK: - Apple Watch Model tests + + func testDeviceModelWatchFirstGen() { + let deviceModel1 = DeviceModel(identifier: Identifier("Watch1,1")) + let deviceModel2 = DeviceModel(identifier: Identifier("Watch1,2")) + XCTAssert(deviceModel1 == .firstGen, "DeviceModel - .firstGen is failing") + XCTAssert(deviceModel2 == .firstGen, "DeviceModel - .firstGen is failing") + } + + func testDeviceModelWatchSeries1() { + let deviceModel1 = DeviceModel(identifier: Identifier("Watch2,6")) + let deviceModel2 = DeviceModel(identifier: Identifier("Watch2,7")) + XCTAssert(deviceModel1 == .series1, "DeviceModel - .series1 is failing") + XCTAssert(deviceModel2 == .series1, "DeviceModel - .series1 is failing") + } + + func testDeviceModelWatchSeries2() { + let deviceModel1 = DeviceModel(identifier: Identifier("Watch2,3")) + let deviceModel2 = DeviceModel(identifier: Identifier("Watch2,4")) + XCTAssert(deviceModel1 == .series2, "DeviceModel - .series2 is failing") + XCTAssert(deviceModel2 == .series2, "DeviceModel - .series2 is failing") + } + + func testDeviceModelWatchSeries3() { + let deviceModel1 = DeviceModel(identifier: Identifier("Watch3,1")) + let deviceModel2 = DeviceModel(identifier: Identifier("Watch3,2")) + let deviceModel3 = DeviceModel(identifier: Identifier("Watch3,3")) + let deviceModel4 = DeviceModel(identifier: Identifier("Watch3,4")) + XCTAssert(deviceModel1 == .series3, "DeviceModel - .series3 is failing") + XCTAssert(deviceModel2 == .series3, "DeviceModel - .series3 is failing") + XCTAssert(deviceModel3 == .series3, "DeviceModel - .series3 is failing") + XCTAssert(deviceModel4 == .series3, "DeviceModel - .series3 is failing") + } + + func testDeviceModelWatchSeries4() { + let deviceModel1 = DeviceModel(identifier: Identifier("Watch4,1")) + let deviceModel2 = DeviceModel(identifier: Identifier("Watch4,2")) + let deviceModel3 = DeviceModel(identifier: Identifier("Watch4,3")) + let deviceModel4 = DeviceModel(identifier: Identifier("Watch4,4")) + XCTAssert(deviceModel1 == .series4, "DeviceModel - .series4 is failing") + XCTAssert(deviceModel2 == .series4, "DeviceModel - .series4 is failing") + XCTAssert(deviceModel3 == .series4, "DeviceModel - .series4 is failing") + XCTAssert(deviceModel4 == .series4, "DeviceModel - .series4 is failing") + } + + func testDeviceModelWatchSeries5() { + let deviceModel1 = DeviceModel(identifier: Identifier("Watch5,1")) + let deviceModel2 = DeviceModel(identifier: Identifier("Watch5,2")) + let deviceModel3 = DeviceModel(identifier: Identifier("Watch5,3")) + let deviceModel4 = DeviceModel(identifier: Identifier("Watch5,4")) + XCTAssert(deviceModel1 == .series5, "DeviceModel - .series5 is failing") + XCTAssert(deviceModel2 == .series5, "DeviceModel - .series5 is failing") + XCTAssert(deviceModel3 == .series5, "DeviceModel - .series5 is failing") + XCTAssert(deviceModel4 == .series5, "DeviceModel - .series5 is failing") + } + + func testDeviceModelWatchSE() { + let deviceModel1 = DeviceModel(identifier: Identifier("Watch5,9")) + let deviceModel2 = DeviceModel(identifier: Identifier("Watch5,10")) + let deviceModel3 = DeviceModel(identifier: Identifier("Watch5,11")) + let deviceModel4 = DeviceModel(identifier: Identifier("Watch5,12")) + XCTAssert(deviceModel1 == .se, "DeviceModel - .se is failing") + XCTAssert(deviceModel2 == .se, "DeviceModel - .se is failing") + XCTAssert(deviceModel3 == .se, "DeviceModel - .se is failing") + XCTAssert(deviceModel4 == .se, "DeviceModel - .se is failing") + } + + func testDeviceModelWatchSeries6() { + let deviceModel1 = DeviceModel(identifier: Identifier("Watch6,1")) + let deviceModel2 = DeviceModel(identifier: Identifier("Watch6,2")) + let deviceModel3 = DeviceModel(identifier: Identifier("Watch6,3")) + let deviceModel4 = DeviceModel(identifier: Identifier("Watch6,4")) + XCTAssert(deviceModel1 == .series6, "DeviceModel - .series6 is failing") + XCTAssert(deviceModel2 == .series6, "DeviceModel - .series6 is failing") + XCTAssert(deviceModel3 == .series6, "DeviceModel - .series6 is failing") + XCTAssert(deviceModel4 == .series6, "DeviceModel - .series6 is failing") + } + + func testDeviceModelWatchSeries7() { + let deviceModel1 = DeviceModel(identifier: Identifier("Watch6,6")) + let deviceModel2 = DeviceModel(identifier: Identifier("Watch6,7")) + let deviceModel3 = DeviceModel(identifier: Identifier("Watch6,8")) + let deviceModel4 = DeviceModel(identifier: Identifier("Watch6,9")) + XCTAssert(deviceModel1 == .series7, "DeviceModel - .series7 is failing") + XCTAssert(deviceModel2 == .series7, "DeviceModel - .series7 is failing") + XCTAssert(deviceModel3 == .series7, "DeviceModel - .series7 is failing") + XCTAssert(deviceModel4 == .series7, "DeviceModel - .series7 is failing") + } + + func testDeviceModelWatchSE2() { + let deviceModel1 = DeviceModel(identifier: Identifier("Watch6,10")) + let deviceModel2 = DeviceModel(identifier: Identifier("Watch6,11")) + let deviceModel3 = DeviceModel(identifier: Identifier("Watch6,12")) + let deviceModel4 = DeviceModel(identifier: Identifier("Watch6,13")) + XCTAssert(deviceModel1 == .se2, "DeviceModel - .se2 is failing") + XCTAssert(deviceModel2 == .se2, "DeviceModel - .se2 is failing") + XCTAssert(deviceModel3 == .se2, "DeviceModel - .se2 is failing") + XCTAssert(deviceModel4 == .se2, "DeviceModel - .se2 is failing") + } + + func testDeviceModelWatchSeries8() { + let deviceModel1 = DeviceModel(identifier: Identifier("Watch6,14")) + let deviceModel2 = DeviceModel(identifier: Identifier("Watch6,15")) + let deviceModel3 = DeviceModel(identifier: Identifier("Watch6,16")) + let deviceModel4 = DeviceModel(identifier: Identifier("Watch6,17")) + XCTAssert(deviceModel1 == .series8, "DeviceModel - .series8 is failing") + XCTAssert(deviceModel2 == .series8, "DeviceModel - .series8 is failing") + XCTAssert(deviceModel3 == .series8, "DeviceModel - .series8 is failing") + XCTAssert(deviceModel4 == .series8, "DeviceModel - .series8 is failing") + } + + func testDeviceModelWatchUltra() { + let deviceModel = DeviceModel(identifier: Identifier("Watch6,18")) + XCTAssert(deviceModel == .ultra, "DeviceModel - .ultra is failing") + } + + func testDeviceModelWatchSeries9() { + let deviceModel1 = DeviceModel(identifier: Identifier("Watch7,1")) + let deviceModel2 = DeviceModel(identifier: Identifier("Watch7,2")) + let deviceModel3 = DeviceModel(identifier: Identifier("Watch7,3")) + let deviceModel4 = DeviceModel(identifier: Identifier("Watch7,4")) + XCTAssert(deviceModel1 == .series9, "DeviceModel - .series9 is failing") + XCTAssert(deviceModel2 == .series9, "DeviceModel - .series9 is failing") + XCTAssert(deviceModel3 == .series9, "DeviceModel - .series9 is failing") + XCTAssert(deviceModel4 == .series9, "DeviceModel - .series9 is failing") + } + + func testDeviceModelWatchUltra2() { + let deviceModel = DeviceModel(identifier: Identifier("Watch7,5")) + XCTAssert(deviceModel == .ultra2, "DeviceModel - .ultra2 is failing") + } + + #endif } diff --git a/Tests/IdentifierTests.swift b/Tests/IdentifierTests.swift index 1369805..dd37292 100644 --- a/Tests/IdentifierTests.swift +++ b/Tests/IdentifierTests.swift @@ -63,7 +63,7 @@ class IdentifierTests: XCTestCase { ) } - + #if os(iOS) // MARK: - iPhone String Description tests func testDisplayStringiPhone16v2() { @@ -611,4 +611,184 @@ class IdentifierTests: XCTestCase { func testDisplayStringiPad1v1() { XCTAssert(Identifier("iPad1,1").description == "iPad", "iPad1,1 is failing to produce a common device model string") } + #endif + + #if os(watchOS) + func testDisplayStringWatch1v1() { + XCTAssert(Identifier("Watch1,1").description == "Apple Watch (1st generation), 38mm case", "Watch1,1 is failing to produce a common device model string") + } + + func testDisplayStringWatch1v2() { + XCTAssert(Identifier("Watch1,2").description == "Apple Watch (1st generation), 42mm case", "Watch1,2 is failing to produce a common device model string") + } + + func testDisplayStringWatch2v3() { + XCTAssert(Identifier("Watch2,3").description == "Apple Watch Series 2, 38mm case", "Watch2,3 is failing to produce a common device model string") + } + + func testDisplayStringWatch2v4() { + XCTAssert(Identifier("Watch2,4").description == "Apple Watch Series 2, 42mm case", "Watch2,4 is failing to produce a common device model string") + } + + func testDisplayStringWatch2v6() { + XCTAssert(Identifier("Watch2,6").description == "Apple Watch Series 1, 38mm case", "Watch2,6 is failing to produce a common device model string") + } + + func testDisplayStringWatch2v7() { + XCTAssert(Identifier("Watch2,7").description == "Apple Watch Series 1, 42mm case", "Watch2,7 is failing to produce a common device model string") + } + + func testDisplayStringWatch3v1() { + XCTAssert(Identifier("Watch3,1").description == "Apple Watch Series 3, 38mm case (GPS + Cellular)", "Watch3,1 is failing to produce a common device model string") + } + + func testDisplayStringWatch3v2() { + XCTAssert(Identifier("Watch3,2").description == "Apple Watch Series 3, 42mm case (GPS + Cellular)", "Watch3,2 is failing to produce a common device model string") + } + + func testDisplayStringWatch3v3() { + XCTAssert(Identifier("Watch3,3").description == "Apple Watch Series 3, 38mm case (GPS)", "Watch3,3 is failing to produce a common device model string") + } + + func testDisplayStringWatch3v4() { + XCTAssert(Identifier("Watch3,4").description == "Apple Watch Series 3, 42mm case (GPS)", "Watch3,4 is failing to produce a common device model string") + } + + func testDisplayStringWatch4v1() { + XCTAssert(Identifier("Watch4,1").description == "Apple Watch Series 4, 40mm case (GPS)", "Watch4,1 is failing to produce a common device model string") + } + + func testDisplayStringWatch4v2() { + XCTAssert(Identifier("Watch4,2").description == "Apple Watch Series 4, 44mm case (GPS)", "Watch4,2 is failing to produce a common device model string") + } + + func testDisplayStringWatch4v3() { + XCTAssert(Identifier("Watch4,3").description == "Apple Watch Series 4, 40mm case (GPS + Cellular)", "Watch4,3 is failing to produce a common device model string") + } + + func testDisplayStringWatch4v4() { + XCTAssert(Identifier("Watch4,4").description == "Apple Watch Series 4, 44mm case (GPS + Cellular)", "Watch4,4 is failing to produce a common device model string") + } + + func testDisplayStringWatch5v1() { + XCTAssert(Identifier("Watch5,1").description == "Apple Watch Series 5, 40mm case (GPS)", "Watch5,1 is failing to produce a common device model string") + } + + func testDisplayStringWatch5v2() { + XCTAssert(Identifier("Watch5,2").description == "Apple Watch Series 5, 44mm case (GPS)", "Watch5,2 is failing to produce a common device model string") + } + + func testDisplayStringWatch5v3() { + XCTAssert(Identifier("Watch5,3").description == "Apple Watch Series 5, 40mm case (GPS + Cellular)", "Watch5,3 is failing to produce a common device model string") + } + + func testDisplayStringWatch5v4() { + XCTAssert(Identifier("Watch5,4").description == "Apple Watch Series 5, 44mm case (GPS + Cellular)", "Watch5,4 is failing to produce a common device model string") + } + + func testDisplayStringWatch5v9() { + XCTAssert(Identifier("Watch5,9").description == "Apple Watch SE, 40mm case (GPS)", "Watch5,9 is failing to produce a common device model string") + } + + func testDisplayStringWatch5v10() { + XCTAssert(Identifier("Watch5,10").description == "Apple Watch SE, 44mm case (GPS)", "Watch5,10 is failing to produce a common device model string") + } + + func testDisplayStringWatch5v11() { + XCTAssert(Identifier("Watch5,11").description == "Apple Watch SE, 40mm case (GPS + Cellular)", "Watch5,11 is failing to produce a common device model string") + } + + func testDisplayStringWatch5v12() { + XCTAssert(Identifier("Watch5,12").description == "Apple Watch SE, 44mm case (GPS + Cellular)", "Watch5,12 is failing to produce a common device model string") + } + + func testDisplayStringWatch6v1() { + XCTAssert(Identifier("Watch6,1").description == "Apple Watch Series 6, 40mm case (GPS)", "Watch6,1 is failing to produce a common device model string") + } + + func testDisplayStringWatch6v2() { + XCTAssert(Identifier("Watch6,2").description == "Apple Watch Series 6, 44mm case (GPS)", "Watch6,2 is failing to produce a common device model string") + } + + func testDisplayStringWatch6v3() { + XCTAssert(Identifier("Watch6,3").description == "Apple Watch Series 6, 40mm case (GPS + Cellular)", "Watch6,3 is failing to produce a common device model string") + } + + func testDisplayStringWatch6v4() { + XCTAssert(Identifier("Watch6,4").description == "Apple Watch Series 6, 44mm case (GPS + Cellular)", "Watch6,4 is failing to produce a common device model string") + } + + func testDisplayStringWatch6v6() { + XCTAssert(Identifier("Watch6,6").description == "Apple Watch Series 7, 41mm case (GPS)", "Watch6,6 is failing to produce a common device model string") + } + + func testDisplayStringWatch6v7() { + XCTAssert(Identifier("Watch6,7").description == "Apple Watch Series 7, 45mm case (GPS)", "Watch6,7 is failing to produce a common device model string") + } + + func testDisplayStringWatch6v8() { + XCTAssert(Identifier("Watch6,8").description == "Apple Watch Series 7, 41mm case (GPS + Cellular)", "Watch6,8 is failing to produce a common device model string") + } + + func testDisplayStringWatch6v9() { + XCTAssert(Identifier("Watch6,9").description == "Apple Watch Series 7, 45mm case (GPS + Cellular)", "Watch6,9 is failing to produce a common device model string") + } + + func testDisplayStringWatch6v10() { + XCTAssert(Identifier("Watch6,10").description == "Apple Watch SE (2nd Generation), 40mm case (GPS)", "Watch6,10 is failing to produce a common device model string") + } + + func testDisplayStringWatch6v11() { + XCTAssert(Identifier("Watch6,11").description == "Apple Watch SE (2nd Generation), 44mm case (GPS)", "Watch6,11 is failing to produce a common device model string") + } + + func testDisplayStringWatch6v12() { + XCTAssert(Identifier("Watch6,12").description == "Apple Watch SE (2nd Generation), 40mm case (GPS + Cellular)", "Watch6,12 is failing to produce a common device model string") + } + + func testDisplayStringWatch6v13() { + XCTAssert(Identifier("Watch6,13").description == "Apple Watch SE (2nd Generation), 44mm case (GPS + Cellular)", "Watch6,13 is failing to produce a common device model string") + } + + func testDisplayStringWatch6v14() { + XCTAssert(Identifier("Watch6,14").description == "Apple Watch Series 8, 41mm case (GPS)", "Watch6,14 is failing to produce a common device model string") + } + + func testDisplayStringWatch6v15() { + XCTAssert(Identifier("Watch6,15").description == "Apple Watch Series 8, 45mm case (GPS)", "Watch6,15 is failing to produce a common device model string") + } + + func testDisplayStringWatch6v16() { + XCTAssert(Identifier("Watch6,16").description == "Apple Watch Series 8, 41mm case (GPS + Cellular)", "Watch6,16 is failing to produce a common device model string") + } + + func testDisplayStringWatch6v17() { + XCTAssert(Identifier("Watch6,17").description == "Apple Watch Series 8, 45mm case (GPS + Cellular)", "Watch6,17 is failing to produce a common device model string") + } + + func testDisplayStringWatch6v18() { + XCTAssert(Identifier("Watch6,18").description == "Apple Watch Ultra", "Watch6,18 is failing to produce a common device model string") + } + + func testDisplayStringWatch7v1() { + XCTAssert(Identifier("Watch7,1").description == "Apple Watch Series 9, 41mm case (GPS)", "Watch7,1 is failing to produce a common device model string") + } + + func testDisplayStringWatch7v2() { + XCTAssert(Identifier("Watch7,2").description == "Apple Watch Series 9, 45mm case (GPS)", "Watch7,2 is failing to produce a common device model string") + } + + func testDisplayStringWatch7v3() { + XCTAssert(Identifier("Watch7,3").description == "Apple Watch Series 9, 41mm case (GPS + Cellular)", "Watch7,3 is failing to produce a common device model string") + } + + func testDisplayStringWatch7v4() { + XCTAssert(Identifier("Watch7,4").description == "Apple Watch Series 9, 45mm case (GPS + Cellular)", "Watch7,4 is failing to produce a common device model string") + } + + func testDisplayStringWatch7v5() { + XCTAssert(Identifier("Watch7,5").description == "Apple Watch Ultra 2", "Watch7,5 is failing to produce a common device model string") + } + + #endif } diff --git a/Tests/UIDeviceExtensionsTests.swift b/Tests/UIDeviceExtensionsTests.swift index f53d6b9..543b1cc 100644 --- a/Tests/UIDeviceExtensionsTests.swift +++ b/Tests/UIDeviceExtensionsTests.swift @@ -24,9 +24,17 @@ @testable import UIDeviceComplete import XCTest +#if os(watchOS) +import WatchKit +#endif + class UIDeviceExtensionsTests: XCTestCase { + #if os(iOS) let DeviceExtensions = UIDeviceComplete(UIDevice()) + #elseif os(watchOS) + let DeviceExtensions = UIDeviceComplete(WKInterfaceDevice()) + #endif func testDeviceExtensionsDeviceFamily() { XCTAssertNotEqual(.unknown, DeviceExtensions.deviceFamily, "DeviceExtensions - .deviceFamily is failing") diff --git a/UIDeviceComplete.xcodeproj/project.pbxproj b/UIDeviceComplete.xcodeproj/project.pbxproj index 4b73f4f..5b1fc17 100644 --- a/UIDeviceComplete.xcodeproj/project.pbxproj +++ b/UIDeviceComplete.xcodeproj/project.pbxproj @@ -20,14 +20,7 @@ AA1C21AB1F385C2A0095BFBD /* UIDeviceExtensionsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA1C21A71F385C2A0095BFBD /* UIDeviceExtensionsTests.swift */; }; AAB1EF351F13866F003BBCF2 /* UIDeviceComplete.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = AAB1EF2B1F13866F003BBCF2 /* UIDeviceComplete.framework */; }; AAB1EF3C1F13866F003BBCF2 /* UIDeviceComplete.h in Headers */ = {isa = PBXBuildFile; fileRef = AAB1EF2E1F13866F003BBCF2 /* UIDeviceComplete.h */; settings = {ATTRIBUTES = (Public, ); }; }; - BF72D63629880FCF00B70A2B /* System.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA1C219A1F385BEC0095BFBD /* System.swift */; }; BFA9121C215B9881000CD8A8 /* DeviceModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFA9121B215B9881000CD8A8 /* DeviceModelTests.swift */; }; - BFE460D02AB5A08700BE39DB /* Identifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA1C21981F385BEC0095BFBD /* Identifier.swift */; }; - BFE460D12AB5A08700BE39DB /* Screen.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA1C21991F385BEC0095BFBD /* Screen.swift */; }; - BFE460D22AB5A08700BE39DB /* DeviceFamily.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA1C21961F385BEC0095BFBD /* DeviceFamily.swift */; }; - BFE460D32AB5A08700BE39DB /* DeviceModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA1C21971F385BEC0095BFBD /* DeviceModel.swift */; }; - BFE460D42AB5A08C00BE39DB /* UIDeviceExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA1C219C1F385BEC0095BFBD /* UIDeviceExtensions.swift */; }; - BFE460D52AB5A08C00BE39DB /* UIDeviceComplete.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA1C219B1F385BEC0095BFBD /* UIDeviceComplete.swift */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -57,7 +50,6 @@ AAB1EF2F1F13866F003BBCF2 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; AAB1EF341F13866F003BBCF2 /* UIDeviceCompleteTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = UIDeviceCompleteTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; AAB1EF3B1F13866F003BBCF2 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - BF72D62E29880FAC00B70A2B /* WKDeviceComplete.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = WKDeviceComplete.framework; sourceTree = BUILT_PRODUCTS_DIR; }; BFA9121B215B9881000CD8A8 /* DeviceModelTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = DeviceModelTests.swift; path = Tests/DeviceModelTests.swift; sourceTree = SOURCE_ROOT; }; /* End PBXFileReference section */ @@ -77,13 +69,6 @@ ); runOnlyForDeploymentPostprocessing = 0; }; - BF72D62B29880FAC00B70A2B /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ @@ -101,7 +86,6 @@ children = ( AAB1EF2B1F13866F003BBCF2 /* UIDeviceComplete.framework */, AAB1EF341F13866F003BBCF2 /* UIDeviceCompleteTests.xctest */, - BF72D62E29880FAC00B70A2B /* WKDeviceComplete.framework */, ); name = Products; sourceTree = ""; @@ -154,13 +138,6 @@ ); runOnlyForDeploymentPostprocessing = 0; }; - BF72D62929880FAC00B70A2B /* Headers */ = { - isa = PBXHeadersBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; /* End PBXHeadersBuildPhase section */ /* Begin PBXNativeTarget section */ @@ -200,24 +177,6 @@ productReference = AAB1EF341F13866F003BBCF2 /* UIDeviceCompleteTests.xctest */; productType = "com.apple.product-type.bundle.unit-test"; }; - BF72D62D29880FAC00B70A2B /* WKDeviceComplete */ = { - isa = PBXNativeTarget; - buildConfigurationList = BF72D63429880FAC00B70A2B /* Build configuration list for PBXNativeTarget "WKDeviceComplete" */; - buildPhases = ( - BF72D62929880FAC00B70A2B /* Headers */, - BF72D62A29880FAC00B70A2B /* Sources */, - BF72D62B29880FAC00B70A2B /* Frameworks */, - BF72D62C29880FAC00B70A2B /* Resources */, - ); - buildRules = ( - ); - dependencies = ( - ); - name = WKDeviceComplete; - productName = WKDeviceComplete; - productReference = BF72D62E29880FAC00B70A2B /* WKDeviceComplete.framework */; - productType = "com.apple.product-type.framework"; - }; /* End PBXNativeTarget section */ /* Begin PBXProject section */ @@ -238,10 +197,6 @@ LastSwiftMigration = 1020; ProvisioningStyle = Automatic; }; - BF72D62D29880FAC00B70A2B = { - CreatedOnToolsVersion = 14.2; - ProvisioningStyle = Automatic; - }; }; }; buildConfigurationList = AAB1EF251F13866F003BBCF2 /* Build configuration list for PBXProject "UIDeviceComplete" */; @@ -259,7 +214,6 @@ targets = ( AAB1EF2A1F13866F003BBCF2 /* UIDeviceComplete */, AAB1EF331F13866F003BBCF2 /* UIDeviceCompleteTests */, - BF72D62D29880FAC00B70A2B /* WKDeviceComplete */, ); }; /* End PBXProject section */ @@ -279,13 +233,6 @@ ); runOnlyForDeploymentPostprocessing = 0; }; - BF72D62C29880FAC00B70A2B /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; /* End PBXResourcesBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ @@ -315,20 +262,6 @@ ); runOnlyForDeploymentPostprocessing = 0; }; - BF72D62A29880FAC00B70A2B /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - BFE460D02AB5A08700BE39DB /* Identifier.swift in Sources */, - BFE460D52AB5A08C00BE39DB /* UIDeviceComplete.swift in Sources */, - BFE460D42AB5A08C00BE39DB /* UIDeviceExtensions.swift in Sources */, - BF72D63629880FCF00B70A2B /* System.swift in Sources */, - BFE460D32AB5A08700BE39DB /* DeviceModel.swift in Sources */, - BFE460D12AB5A08700BE39DB /* Screen.swift in Sources */, - BFE460D22AB5A08700BE39DB /* DeviceFamily.swift in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; /* End PBXSourcesBuildPhase section */ /* Begin PBXTargetDependency section */ @@ -479,10 +412,12 @@ PRODUCT_BUNDLE_IDENTIFIER = nmaccharoli.UIDeviceComplete; PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator watchos watchsimulator"; SUPPORTS_MACCATALYST = NO; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; + TARGETED_DEVICE_FAMILY = "1,2,4"; + WATCHOS_DEPLOYMENT_TARGET = 4.0; }; name = Debug; }; @@ -503,9 +438,11 @@ PRODUCT_BUNDLE_IDENTIFIER = nmaccharoli.UIDeviceComplete; PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator watchos watchsimulator"; SUPPORTS_MACCATALYST = NO; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; + TARGETED_DEVICE_FAMILY = "1,2,4"; + WATCHOS_DEPLOYMENT_TARGET = 4.0; }; name = Release; }; @@ -521,8 +458,12 @@ PRODUCT_BUNDLE_IDENTIFIER = nmaccharoli.UIDeviceCompleteTests; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator watchos watchsimulator"; + SUPPORTS_MACCATALYST = YES; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2,4"; + WATCHOS_DEPLOYMENT_TARGET = 4.0; }; name = Debug; }; @@ -538,67 +479,10 @@ PRODUCT_BUNDLE_IDENTIFIER = nmaccharoli.UIDeviceCompleteTests; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator watchos watchsimulator"; + SUPPORTS_MACCATALYST = YES; SWIFT_VERSION = 5.0; - }; - name = Release; - }; - BF72D63229880FAC00B70A2B /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; - CLANG_ENABLE_OBJC_WEAK = YES; - CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; - CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; - DEFINES_MODULE = YES; - DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 1; - DYLIB_INSTALL_NAME_BASE = "@rpath"; - GCC_C_LANGUAGE_STANDARD = gnu11; - GENERATE_INFOPLIST_FILE = YES; - INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2023 Nicholas Maccharoli. All rights reserved."; - INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - MARKETING_VERSION = 1.0; - MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; - MTL_FAST_MATH = YES; - PRODUCT_BUNDLE_IDENTIFIER = "com.vincent-neo.WKDeviceComplete"; - PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; - SDKROOT = watchos; - SKIP_INSTALL = YES; - SWIFT_EMIT_LOC_STRINGS = YES; - SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = 4; - WATCHOS_DEPLOYMENT_TARGET = 4.0; - }; - name = Debug; - }; - BF72D63329880FAC00B70A2B /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; - CLANG_ENABLE_OBJC_WEAK = YES; - CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; - CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; - DEFINES_MODULE = YES; - DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 1; - DYLIB_INSTALL_NAME_BASE = "@rpath"; - GCC_C_LANGUAGE_STANDARD = gnu11; - GENERATE_INFOPLIST_FILE = YES; - INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2023 Nicholas Maccharoli. All rights reserved."; - INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - MARKETING_VERSION = 1.0; - MTL_FAST_MATH = YES; - PRODUCT_BUNDLE_IDENTIFIER = "com.vincent-neo.WKDeviceComplete"; - PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; - SDKROOT = watchos; - SKIP_INSTALL = YES; - SWIFT_EMIT_LOC_STRINGS = YES; - SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = 4; + TARGETED_DEVICE_FAMILY = "1,2,4"; WATCHOS_DEPLOYMENT_TARGET = 4.0; }; name = Release; @@ -633,15 +517,6 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; - BF72D63429880FAC00B70A2B /* Build configuration list for PBXNativeTarget "WKDeviceComplete" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - BF72D63229880FAC00B70A2B /* Debug */, - BF72D63329880FAC00B70A2B /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; /* End XCConfigurationList section */ }; rootObject = AAB1EF221F13866F003BBCF2 /* Project object */; From a4a2e1ce66d5069d6bf0c3b2acc7409c9af43f7d Mon Sep 17 00:00:00 2001 From: Vincent Neo <23420208+vincentneo@users.noreply.github.com> Date: Thu, 21 Sep 2023 18:49:09 +0800 Subject: [PATCH 5/5] include testing for watchOS build, via actions --- .github/workflows/swift.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/swift.yml b/.github/workflows/swift.yml index ce48216..4b145e9 100644 --- a/.github/workflows/swift.yml +++ b/.github/workflows/swift.yml @@ -10,5 +10,7 @@ jobs: DEVELOPER_DIR: /Applications/Xcode_14.0.app/Contents/Developer steps: - uses: actions/checkout@v2 - - name: Test + - name: Test (iOS) run: set -o pipefail && env NSUnbufferedIO=YES xcodebuild -project "UIDeviceComplete.xcodeproj" -scheme "UIDeviceComplete" -destination "OS=16.0,name=iPhone 14 Pro" clean test | xcpretty + - name: Test (watchOS) + run: set -o pipefail && env NSUnbufferedIO=YES xcodebuild -project "UIDeviceComplete.xcodeproj" -scheme "UIDeviceComplete" -destination "OS=9.0,name=Apple Watch Series 8 (45mm)" clean test | xcpretty