diff --git a/Networking/Sources/Core/ApplicationPassword/ApplicationPasswordUseCase.swift b/Networking/Sources/Core/ApplicationPassword/ApplicationPasswordUseCase.swift index d663acccb4f..3f45858302e 100644 --- a/Networking/Sources/Core/ApplicationPassword/ApplicationPasswordUseCase.swift +++ b/Networking/Sources/Core/ApplicationPassword/ApplicationPasswordUseCase.swift @@ -29,6 +29,25 @@ public protocol ApplicationPasswordUseCase { func deletePassword() async throws } +/// A wrapper for the `UIDevice` `model` and `identifierForVendor` properties. +/// +/// This is necessary because `UIDevice` is part of UIKit which we cannot use when targeting watchOS. +/// So, to keep this package compatible with watchOS, we need to abstract UIKit away and delegate it to the consumers to provide us +/// with the device information. +/// +/// This approach is feasible because only the `applicationPasswordName` method in +/// `DefaultApplicationPasswordUseCase` needs access to the information and watchOS does not need to create application +/// passwords. We can therefore pass a `nil` value to it to satisfy the compilation without issues for the user experience. +public struct DeviceModelIdentifierInfo { + let model: String + let identifierForVendor: String + + public init(model: String, identifierForVendor: String) { + self.model = model + self.identifierForVendor = identifierForVendor + } +} + final public class DefaultApplicationPasswordUseCase: ApplicationPasswordUseCase { /// Site Address /// @@ -46,30 +65,31 @@ final public class DefaultApplicationPasswordUseCase: ApplicationPasswordUseCase /// private let storage: ApplicationPasswordStorage + private let deviceModelIdentifierInfo: DeviceModelIdentifierInfo? + /// Used to name the password in wpadmin. /// private var applicationPasswordName: String { - get async { -#if !os(watchOS) + get { + guard let deviceModelIdentifierInfo else { + return "" // This is not needed on watchOS as the watch does not create application passwords. + } + let bundleIdentifier = Bundle.main.bundleIdentifier ?? "Unknown" - let model = await UIDevice.current.model - let identifierForVendor = await UIDevice.current.identifierForVendor?.uuidString ?? "" - return "\(bundleIdentifier).ios-app-client.\(model).\(identifierForVendor)" -#endif -#if os(watchOS) - return "" // This is not needed on watchOS as the watch does not create application passwords. -#endif + return "\(bundleIdentifier).ios-app-client.\(deviceModelIdentifierInfo.model).\(deviceModelIdentifierInfo.identifierForVendor)" } } public init(username: String, password: String, siteAddress: String, + deviceModelIdentifierInfo: DeviceModelIdentifierInfo? = nil, network: Network? = nil, keychain: Keychain = Keychain(service: WooConstants.keychainServiceName)) throws { self.siteAddress = siteAddress self.username = username self.storage = ApplicationPasswordStorage(keychain: keychain) + self.deviceModelIdentifierInfo = deviceModelIdentifierInfo if let network { self.network = network diff --git a/WooCommerce/Classes/Authentication/AuthenticationManager.swift b/WooCommerce/Classes/Authentication/AuthenticationManager.swift index b8a7d2162e1..7fd88172de4 100644 --- a/WooCommerce/Classes/Authentication/AuthenticationManager.swift +++ b/WooCommerce/Classes/Authentication/AuthenticationManager.swift @@ -4,6 +4,7 @@ import KeychainAccess import WordPressAuthenticator import WordPressUI import Yosemite +import UIKit import class Networking.UserAgent import enum Experiments.ABTest import struct Networking.Settings @@ -751,7 +752,8 @@ private extension AuthenticationManager { guard let useCase = try? DefaultApplicationPasswordUseCase( username: siteCredentials.username, password: siteCredentials.password, - siteAddress: siteCredentials.siteURL + siteAddress: siteCredentials.siteURL, + deviceModelIdentifierInfo: UIDevice.current.deviceModelIdentifierInfo ) else { return assertionFailure("⛔️ Error creating application password use case") } diff --git a/WooCommerce/Classes/System/SessionManager.swift b/WooCommerce/Classes/System/SessionManager.swift index 5de12f0cf88..74c6e52df4f 100644 --- a/WooCommerce/Classes/System/SessionManager.swift +++ b/WooCommerce/Classes/System/SessionManager.swift @@ -1,6 +1,7 @@ import Combine import Foundation import Yosemite +import UIKit import KeychainAccess import protocol Networking.ApplicationPasswordUseCase import class Networking.OneTimeApplicationPasswordUseCase @@ -233,6 +234,7 @@ final class SessionManager: SessionManagerProtocol { return try? DefaultApplicationPasswordUseCase(username: username, password: password, siteAddress: siteAddress, + deviceModelIdentifierInfo: UIDevice.current.deviceModelIdentifierInfo, keychain: keychain) case let .applicationPassword(_, _, siteAddress): return OneTimeApplicationPasswordUseCase(siteAddress: siteAddress, keychain: keychain) diff --git a/WooCommerce/Classes/System/UIDevice+DeviceModelIdentifierInfo.swift b/WooCommerce/Classes/System/UIDevice+DeviceModelIdentifierInfo.swift new file mode 100644 index 00000000000..a23e6dfa5aa --- /dev/null +++ b/WooCommerce/Classes/System/UIDevice+DeviceModelIdentifierInfo.swift @@ -0,0 +1,12 @@ +import Networking +import UIKit + +extension UIDevice { + + var deviceModelIdentifierInfo: DeviceModelIdentifierInfo { + DeviceModelIdentifierInfo( + model: model, + identifierForVendor: identifierForVendor?.uuidString ?? "" + ) + } +} diff --git a/WooCommerce/WooCommerce.xcodeproj/project.pbxproj b/WooCommerce/WooCommerce.xcodeproj/project.pbxproj index 457c6678834..0da4815405a 100644 --- a/WooCommerce/WooCommerce.xcodeproj/project.pbxproj +++ b/WooCommerce/WooCommerce.xcodeproj/project.pbxproj @@ -1304,6 +1304,7 @@ 3F58701F281B947E004F7556 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 3F58701E281B947E004F7556 /* Main.storyboard */; }; 3F587021281B9494004F7556 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 3F587020281B9494004F7556 /* LaunchScreen.storyboard */; }; 3F587026281B9C19004F7556 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 3F587028281B9C19004F7556 /* InfoPlist.strings */; }; + 3F88EC3F2DF8D4BC0023A6F4 /* UIDevice+DeviceModelIdentifierInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3F88EC3E2DF8D4BC0023A6F4 /* UIDevice+DeviceModelIdentifierInfo.swift */; }; 3FA96DF42C94043200CDA78F /* Yosemite.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3FA96DF32C94043200CDA78F /* Yosemite.framework */; }; 3FF314E126FC74450012E68E /* UITestsFoundation.h in Headers */ = {isa = PBXBuildFile; fileRef = 3FF314E026FC74450012E68E /* UITestsFoundation.h */; settings = {ATTRIBUTES = (Public, ); }; }; 3FF314EA26FC751B0012E68E /* XCTest+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = F997173223DBCF2800592D8E /* XCTest+Extensions.swift */; }; @@ -4509,6 +4510,7 @@ 3F587037281B9C50004F7556 /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/InfoPlist.strings; sourceTree = ""; }; 3F587038281B9C52004F7556 /* sv */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sv; path = sv.lproj/InfoPlist.strings; sourceTree = ""; }; 3F64F76C2C06A3A50085DEEF /* WooCommerce.release-alpha.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "WooCommerce.release-alpha.xcconfig"; sourceTree = ""; }; + 3F88EC3E2DF8D4BC0023A6F4 /* UIDevice+DeviceModelIdentifierInfo.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIDevice+DeviceModelIdentifierInfo.swift"; sourceTree = ""; }; 3FA96DF32C94043200CDA78F /* Yosemite.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = Yosemite.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 3FF314D626FC55DE0012E68E /* ScreenObject+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ScreenObject+Extension.swift"; sourceTree = ""; }; 3FF314DE26FC74450012E68E /* UITestsFoundation.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = UITestsFoundation.framework; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -11131,6 +11133,7 @@ DE6906E627D74A1900735E3B /* WooSplitViewController.swift */, 26A7C8782BE91F3D00382627 /* WatchDependenciesSynchronizer.swift */, 26B249702BEC801400730730 /* WatchDependencies.swift */, + 3F88EC3E2DF8D4BC0023A6F4 /* UIDevice+DeviceModelIdentifierInfo.swift */, ); path = System; sourceTree = ""; @@ -16512,6 +16515,7 @@ 02CE4304276993DA0006EAEF /* CaptureDevicePermissionChecker.swift in Sources */, 205B7ECD2C19FD2F00D14A36 /* PointOfSaleCardPresentPaymentDisplayReaderMessageMessageViewModel.swift in Sources */, DEF8CF1F29AC870A00800A60 /* WPComEmailLoginViewModel.swift in Sources */, + 3F88EC3F2DF8D4BC0023A6F4 /* UIDevice+DeviceModelIdentifierInfo.swift in Sources */, 74A33D8021C3F234009E25DE /* LicensesViewController.swift in Sources */, 454453CA27566CDE00464AC5 /* HubMenuViewModel.swift in Sources */, 934CB123224EAB150005CCB9 /* main.swift in Sources */,