diff --git a/Modules/Sources/WooFoundation/Extensions/DateFormatter+Extensions.swift b/Modules/Sources/WooFoundation/Extensions/DateFormatter+Extensions.swift new file mode 100644 index 00000000000..6f94a0b98ba --- /dev/null +++ b/Modules/Sources/WooFoundation/Extensions/DateFormatter+Extensions.swift @@ -0,0 +1,16 @@ +import Foundation + +/// DateFormatter Extensions +/// +extension DateFormatter { + /// Date formatter used for creating a **localized** date and time string. + /// + /// Example output in English: "Jan 28, 2018, 11:23 AM" + /// + public static let dateAndTimeFormatter: DateFormatter = { + let formatter = DateFormatter() + formatter.setLocalizedDateFormatFromTemplate("MMM d yyyy hh:mm a") + + return formatter + }() +} diff --git a/WooCommerce/Classes/Extensions/Decimal+Helpers.swift b/Modules/Sources/WooFoundation/Extensions/Decimal+Extensions.swift similarity index 72% rename from WooCommerce/Classes/Extensions/Decimal+Helpers.swift rename to Modules/Sources/WooFoundation/Extensions/Decimal+Extensions.swift index de6e3b0393d..95f4a1c6231 100644 --- a/WooCommerce/Classes/Extensions/Decimal+Helpers.swift +++ b/Modules/Sources/WooFoundation/Extensions/Decimal+Extensions.swift @@ -1,14 +1,13 @@ import Foundation extension Decimal { - /// Returns the int value of a decimal. We ensure we round up our Decimal before converting it to an Int, using NSDecimalRound. /// - var intValue: Int { + public var intValue: Int { NSDecimalNumber(decimal: whole).intValue } - func rounded(_ roundingMode: NSDecimalNumber.RoundingMode = .up, scale: Int = 0) -> Self { + public func rounded(_ roundingMode: NSDecimalNumber.RoundingMode = .up, scale: Int = 0) -> Self { var result = Self() var number = self NSDecimalRound(&result, &number, scale, roundingMode) @@ -16,7 +15,4 @@ extension Decimal { } private var whole: Self { rounded( self < 0 ? .down : .up) } - - private var fraction: Self { self - whole } - } diff --git a/WooCommerce/Classes/ViewRelated/ReusableViews/SwiftUI Components/SafariSheet.swift b/Modules/Sources/WooFoundation/UI Components/SafariSheet.swift similarity index 76% rename from WooCommerce/Classes/ViewRelated/ReusableViews/SwiftUI Components/SafariSheet.swift rename to Modules/Sources/WooFoundation/UI Components/SafariSheet.swift index c65d90c6e60..39e37518aa6 100644 --- a/WooCommerce/Classes/ViewRelated/ReusableViews/SwiftUI Components/SafariSheet.swift +++ b/Modules/Sources/WooFoundation/UI Components/SafariSheet.swift @@ -3,14 +3,18 @@ import SwiftUI import SafariServices import UIKit -struct SafariSheetView: UIViewControllerRepresentable { - let url: URL +public struct SafariSheetView: UIViewControllerRepresentable { + private let url: URL - func makeUIViewController(context: Context) -> UIViewController { + public init(url: URL) { + self.url = url + } + + public func makeUIViewController(context: Context) -> UIViewController { SFSafariViewController(url: url) } - func updateUIViewController(_ uiViewController: UIViewController, context: Context) { + public func updateUIViewController(_ uiViewController: UIViewController, context: Context) { // nothing to do here } } @@ -20,7 +24,7 @@ extension View { /// Does nothing if the input URL is nil. /// @ViewBuilder - func safariSheet(isPresented: Binding, url: URL?, onDismiss: (() -> Void)? = nil) -> some View { + public func safariSheet(isPresented: Binding, url: URL?, onDismiss: (() -> Void)? = nil) -> some View { if let url = url { sheet(isPresented: isPresented, onDismiss: onDismiss) { SafariSheetView(url: url) @@ -33,7 +37,7 @@ extension View { /// /// When the sheet is dismissed, the binding's value will be set to nil. /// - func safariSheet(url: Binding, onDismiss: (() -> Void)? = nil) -> some View { + public func safariSheet(url: Binding, onDismiss: (() -> Void)? = nil) -> some View { sheet(isPresented: url.notNil(), onDismiss: onDismiss) { if let url = url.wrappedValue { SafariSheetView(url: url) diff --git a/Modules/Sources/WooFoundation/UI Components/SafariView.swift b/Modules/Sources/WooFoundation/UI Components/SafariView.swift new file mode 100644 index 00000000000..3a4c85cee4e --- /dev/null +++ b/Modules/Sources/WooFoundation/UI Components/SafariView.swift @@ -0,0 +1,23 @@ +import SwiftUI +import SafariServices + +/// SwiftUI interface for UIKit SFSafariViewController +/// Provides a visible interface for web browsing, and Safari features +/// +public struct SafariView: UIViewControllerRepresentable { + + private let url: URL + + public init(url: URL) { + self.url = url + } + + public func makeUIViewController(context: UIViewControllerRepresentableContext) -> SFSafariViewController { + return SFSafariViewController(url: url) + } + + public func updateUIViewController(_ uiViewController: SFSafariViewController, + context: UIViewControllerRepresentableContext) { + + } +} diff --git a/WooCommerce/Classes/Tools/SiteAddress.swift b/Modules/Sources/Yosemite/Model/Extensions/CountryCode+readableCountry.swift similarity index 91% rename from WooCommerce/Classes/Tools/SiteAddress.swift rename to Modules/Sources/Yosemite/Model/Extensions/CountryCode+readableCountry.swift index e303769ad62..58fc8152a02 100644 --- a/WooCommerce/Classes/Tools/SiteAddress.swift +++ b/Modules/Sources/Yosemite/Model/Extensions/CountryCode+readableCountry.swift @@ -1,92 +1,13 @@ import Foundation -import Yosemite import WooFoundation -/// Represent and parse the Address of the store, returned in the SiteSettings API `/settings/general/` -/// -final class SiteAddress { - - private let siteSettings: [SiteSetting] - - var address: String { - return getValueFromSiteSettings(Constants.address) ?? "" - } - - var address2: String { - return getValueFromSiteSettings(Constants.address2) ?? "" - } - - var city: String { - return getValueFromSiteSettings(Constants.city) ?? "" - } - - var postalCode: String { - return getValueFromSiteSettings(Constants.postalCode) ?? "" - } - - var countryCode: CountryCode { - guard let countryComponent = getCountryAndStateComponents().first else { - DDLogError("⛔️ Could not determine country code for site address: no country component found.") - return .unknown - } - guard let countryCode = CountryCode(rawValue: countryComponent) else { - DDLogError("⛔️ Could not determine country code for country: \(countryComponent)") - return .unknown - } - return countryCode - } - - /// Returns the name of the country associated with the current store. - /// The default store country is provided in a format like `HK:KOWLOON` - /// This method will transform `HK:KOWLOON` into `Hong Kong` - /// Will return nil if it can not figure out a valid country name - var countryName: String? { - guard countryCode != .unknown else { - return nil - } - - return countryCode.readableCountry - } - - var state: String { - return getCountryAndStateComponents().last ?? "" - } - - init(siteSettings: [SiteSetting] = ServiceLocator.selectedSiteSettings.siteSettings) { - self.siteSettings = siteSettings - } - - private func getCountryAndStateComponents() -> [String] { - getValueFromSiteSettings(Constants.countryAndState)?.components(separatedBy: ":") ?? [] - } - - private func getValueFromSiteSettings(_ settingID: String) -> String? { - return siteSettings.first { (setting) -> Bool in - return setting.settingID == settingID - }?.value - } -} - -// MARK: - Constants. -// -private extension SiteAddress { - /// The key of the SiteSetting containing the store address - enum Constants { - static let address = "woocommerce_store_address" - static let address2 = "woocommerce_store_address_2" - static let city = "woocommerce_store_city" - static let postalCode = "woocommerce_store_postcode" - static let countryAndState = "woocommerce_default_country" - } -} - // MARK: - Mapping between country codes and readable names // The country names were extracted from the response to `/wp-json/wc/v3/settings/general` // The default countries are listed under `woocommerce_default_country` // in one of the following formats: // - `"COUNTRY_CODE": "READABALE_COUNTRY_NAME" // - `"COUNTRY_CODE:COUNTRY_REGION": "READABLE_COUNTRY_NAME - READABLE_COUNTRY_REGION" -extension CountryCode { +public extension CountryCode { var readableCountry: String { switch self { // A diff --git a/Modules/Sources/Yosemite/Tools/Settings/SiteAddress.swift b/Modules/Sources/Yosemite/Tools/Settings/SiteAddress.swift new file mode 100644 index 00000000000..25474fa0ab6 --- /dev/null +++ b/Modules/Sources/Yosemite/Tools/Settings/SiteAddress.swift @@ -0,0 +1,80 @@ +import Foundation +import WooFoundation + +/// Represent and parse the Address of the store, returned in the SiteSettings API `/settings/general/` +/// +public class SiteAddress { + + private let siteSettings: [SiteSetting] + + public var address: String { + return getValueFromSiteSettings(Constants.address) ?? "" + } + + public var address2: String { + return getValueFromSiteSettings(Constants.address2) ?? "" + } + + public var city: String { + return getValueFromSiteSettings(Constants.city) ?? "" + } + + public var postalCode: String { + return getValueFromSiteSettings(Constants.postalCode) ?? "" + } + + public var countryCode: CountryCode { + guard let countryComponent = getCountryAndStateComponents().first else { + DDLogError("⛔️ Could not determine country code for site address: no country component found.") + return .unknown + } + guard let countryCode = CountryCode(rawValue: countryComponent) else { + DDLogError("⛔️ Could not determine country code for country: \(countryComponent)") + return .unknown + } + return countryCode + } + + /// Returns the name of the country associated with the current store. + /// The default store country is provided in a format like `HK:KOWLOON` + /// This method will transform `HK:KOWLOON` into `Hong Kong` + /// Will return nil if it can not figure out a valid country name + public var countryName: String? { + guard countryCode != .unknown else { + return nil + } + + return countryCode.readableCountry + } + + public var state: String { + return getCountryAndStateComponents().last ?? "" + } + + public init(siteSettings: [SiteSetting]) { + self.siteSettings = siteSettings + } + + private func getCountryAndStateComponents() -> [String] { + getValueFromSiteSettings(Constants.countryAndState)?.components(separatedBy: ":") ?? [] + } + + private func getValueFromSiteSettings(_ settingID: String) -> String? { + return siteSettings.first { (setting) -> Bool in + return setting.settingID == settingID + }?.value + } +} + +// MARK: - Constants. +// +private extension SiteAddress { + /// The key of the SiteSetting containing the store address + enum Constants { + static let address = "woocommerce_store_address" + static let address2 = "woocommerce_store_address_2" + static let city = "woocommerce_store_city" + static let postalCode = "woocommerce_store_postcode" + static let countryAndState = "woocommerce_default_country" + } +} diff --git a/WooCommerce/WooCommerceTests/Tools/SiteAddressTests.swift b/Modules/Tests/YosemiteTests/Tools/Settings/SiteAddressTests.swift similarity index 97% rename from WooCommerce/WooCommerceTests/Tools/SiteAddressTests.swift rename to Modules/Tests/YosemiteTests/Tools/Settings/SiteAddressTests.swift index fde48d8450c..a465182d778 100644 --- a/WooCommerce/WooCommerceTests/Tools/SiteAddressTests.swift +++ b/Modules/Tests/YosemiteTests/Tools/Settings/SiteAddressTests.swift @@ -1,6 +1,6 @@ import XCTest -@testable import WooCommerce @testable import Networking +@testable import Yosemite final class SiteAddressTests: XCTestCase { diff --git a/WooCommerce/Classes/Extensions/DateFormatter+Helpers.swift b/WooCommerce/Classes/Extensions/DateFormatter+Helpers.swift index 01d0722bda5..55e34e1ee4c 100644 --- a/WooCommerce/Classes/Extensions/DateFormatter+Helpers.swift +++ b/WooCommerce/Classes/Extensions/DateFormatter+Helpers.swift @@ -81,15 +81,4 @@ extension DateFormatter { formatter.setLocalizedDateFormatFromTemplate("hh:mm a") return formatter }() - - /// Date formatter used for creating a **localized** date and time string. - /// - /// Example output in English: "Jan 28, 2018, 11:23 AM" - /// - public static let dateAndTimeFormatter: DateFormatter = { - let formatter = DateFormatter() - formatter.setLocalizedDateFormatFromTemplate("MMM d yyyy hh:mm a") - - return formatter - }() } diff --git a/WooCommerce/Classes/Extensions/SiteAddress+ServiceLocator.swift b/WooCommerce/Classes/Extensions/SiteAddress+ServiceLocator.swift new file mode 100644 index 00000000000..05280ea80ab --- /dev/null +++ b/WooCommerce/Classes/Extensions/SiteAddress+ServiceLocator.swift @@ -0,0 +1,7 @@ +import class Yosemite.SiteAddress + +extension SiteAddress { + convenience init() { + self.init(siteSettings: ServiceLocator.selectedSiteSettings.siteSettings) + } +} diff --git a/WooCommerce/Classes/POS/Presentation/Settings/POSSettingsStoreViewModel.swift b/WooCommerce/Classes/POS/Presentation/Settings/POSSettingsStoreViewModel.swift index ff3ef27b4ef..dce437b06da 100644 --- a/WooCommerce/Classes/POS/Presentation/Settings/POSSettingsStoreViewModel.swift +++ b/WooCommerce/Classes/POS/Presentation/Settings/POSSettingsStoreViewModel.swift @@ -1,4 +1,5 @@ import SwiftUI +import class Yosemite.SiteAddress import protocol Yosemite.PluginsServiceProtocol import protocol Yosemite.PointOfSaleSettingsServiceProtocol import enum Yosemite.Plugin diff --git a/WooCommerce/Classes/POS/Presentation/Settings/PointOfSaleSettingsHardwareDetailView.swift b/WooCommerce/Classes/POS/Presentation/Settings/PointOfSaleSettingsHardwareDetailView.swift index 4fb6ec1a9f8..6c4c6335409 100644 --- a/WooCommerce/Classes/POS/Presentation/Settings/PointOfSaleSettingsHardwareDetailView.swift +++ b/WooCommerce/Classes/POS/Presentation/Settings/PointOfSaleSettingsHardwareDetailView.swift @@ -1,4 +1,5 @@ import SwiftUI +import struct WooFoundation.SafariView struct PointOfSaleSettingsHardwareDetailView: View { @Environment(\.dynamicTypeSize) private var dynamicTypeSize diff --git a/WooCommerce/Classes/POS/Presentation/Settings/PointOfSaleSettingsHelpDetailView.swift b/WooCommerce/Classes/POS/Presentation/Settings/PointOfSaleSettingsHelpDetailView.swift index 5f7422f8597..1d273d87f63 100644 --- a/WooCommerce/Classes/POS/Presentation/Settings/PointOfSaleSettingsHelpDetailView.swift +++ b/WooCommerce/Classes/POS/Presentation/Settings/PointOfSaleSettingsHelpDetailView.swift @@ -1,4 +1,5 @@ import SwiftUI +import struct WooFoundation.SafariView struct PointOfSaleSettingsHelpDetailView: View { @Environment(\.dynamicTypeSize) private var dynamicTypeSize diff --git a/WooCommerce/Classes/POS/TabBar/POSIneligibleView.swift b/WooCommerce/Classes/POS/TabBar/POSIneligibleView.swift index 6d7e4190372..dd52102442c 100644 --- a/WooCommerce/Classes/POS/TabBar/POSIneligibleView.swift +++ b/WooCommerce/Classes/POS/TabBar/POSIneligibleView.swift @@ -1,4 +1,5 @@ import SwiftUI +import Yosemite /// A view that displays when the Point of Sale (POS) feature is not available for the current store. /// Shows the specific reason why POS is ineligible and provides a button to re-check eligibility. diff --git a/WooCommerce/Classes/ViewRelated/Dashboard/Settings/In-Person Payments/CardReaderManualRowView.swift b/WooCommerce/Classes/ViewRelated/Dashboard/Settings/In-Person Payments/CardReaderManualRowView.swift index 312afa3e8bb..bfc7b4c8cc6 100644 --- a/WooCommerce/Classes/ViewRelated/Dashboard/Settings/In-Person Payments/CardReaderManualRowView.swift +++ b/WooCommerce/Classes/ViewRelated/Dashboard/Settings/In-Person Payments/CardReaderManualRowView.swift @@ -1,4 +1,5 @@ import SwiftUI +import struct WooFoundation.SafariSheetView struct CardReaderManualRowView: View { // Environment safe areas diff --git a/WooCommerce/Classes/ViewRelated/Dashboard/Settings/In-Person Payments/SafariView.swift b/WooCommerce/Classes/ViewRelated/Dashboard/Settings/In-Person Payments/SafariView.swift deleted file mode 100644 index a06f630aed0..00000000000 --- a/WooCommerce/Classes/ViewRelated/Dashboard/Settings/In-Person Payments/SafariView.swift +++ /dev/null @@ -1,19 +0,0 @@ -import SwiftUI -import SafariServices - -/// SwiftUI interface for UIKit SFSafariViewController -/// Provides a visible interface for web browsing, and Safari features -/// -struct SafariView: UIViewControllerRepresentable { - - let url: URL - - func makeUIViewController(context: UIViewControllerRepresentableContext) -> SFSafariViewController { - return SFSafariViewController(url: url) - } - - func updateUIViewController(_ uiViewController: SFSafariViewController, - context: UIViewControllerRepresentableContext) { - - } -} diff --git a/WooCommerce/Classes/ViewRelated/Dashboard/Settings/POS/LegacyPOSTabEligibilityChecker.swift b/WooCommerce/Classes/ViewRelated/Dashboard/Settings/POS/LegacyPOSTabEligibilityChecker.swift index dcb98a8ed94..4ada76ef272 100644 --- a/WooCommerce/Classes/ViewRelated/Dashboard/Settings/POS/LegacyPOSTabEligibilityChecker.swift +++ b/WooCommerce/Classes/ViewRelated/Dashboard/Settings/POS/LegacyPOSTabEligibilityChecker.swift @@ -13,6 +13,7 @@ import enum Yosemite.FeatureFlagAction import enum Yosemite.SettingAction import protocol Yosemite.PluginsServiceProtocol import class Yosemite.PluginsService +import class Yosemite.SiteAddress import class WooFoundation.VersionHelpers /// Legacy enum containing POS invisible reasons + POSIneligibleReason cases for i1. diff --git a/WooCommerce/Classes/ViewRelated/Dashboard/Settings/POS/POSTabEligibilityChecker.swift b/WooCommerce/Classes/ViewRelated/Dashboard/Settings/POS/POSTabEligibilityChecker.swift index 5784d6e50fd..7a8094c9265 100644 --- a/WooCommerce/Classes/ViewRelated/Dashboard/Settings/POS/POSTabEligibilityChecker.swift +++ b/WooCommerce/Classes/ViewRelated/Dashboard/Settings/POS/POSTabEligibilityChecker.swift @@ -15,6 +15,7 @@ import protocol Yosemite.POSSystemStatusServiceProtocol import class Yosemite.POSSystemStatusService import protocol Yosemite.POSSiteSettingServiceProtocol import class Yosemite.POSSiteSettingService +import class Yosemite.SiteAddress import enum Networking.SiteSettingsFeature import class WooFoundation.VersionHelpers diff --git a/WooCommerce/Classes/ViewRelated/Orders/Order Details/WCShip Installation Process/WCShipCTAView.swift b/WooCommerce/Classes/ViewRelated/Orders/Order Details/WCShip Installation Process/WCShipCTAView.swift index d288af0ea98..0eb7ce9bd69 100644 --- a/WooCommerce/Classes/ViewRelated/Orders/Order Details/WCShip Installation Process/WCShipCTAView.swift +++ b/WooCommerce/Classes/ViewRelated/Orders/Order Details/WCShip Installation Process/WCShipCTAView.swift @@ -1,4 +1,5 @@ import SwiftUI +import struct WooFoundation.SafariSheetView final class WCShipCTAHostingController: UIHostingController { diff --git a/WooCommerce/WooCommerce.xcodeproj/project.pbxproj b/WooCommerce/WooCommerce.xcodeproj/project.pbxproj index 35dcc9f2288..7b6ef7bb5a6 100644 --- a/WooCommerce/WooCommerce.xcodeproj/project.pbxproj +++ b/WooCommerce/WooCommerce.xcodeproj/project.pbxproj @@ -526,6 +526,7 @@ 029F29FC24D94106004751CA /* EditableProductVariationModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 029F29FB24D94106004751CA /* EditableProductVariationModel.swift */; }; 029F29FE24DA5B2D004751CA /* ProductInventorySettingsViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 029F29FD24DA5B2D004751CA /* ProductInventorySettingsViewModel.swift */; }; 029F53182BEB33BC00E31A10 /* CollapsibleCustomerCardAddressView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 029F53172BEB33BC00E31A10 /* CollapsibleCustomerCardAddressView.swift */; }; + 02A239742E811A920067AB38 /* SiteAddress+ServiceLocator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 02A239732E811A690067AB38 /* SiteAddress+ServiceLocator.swift */; }; 02A275BA23FE50AA005C560F /* ProductUIImageLoader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 02A275B923FE50AA005C560F /* ProductUIImageLoader.swift */; }; 02A275BE23FE57DC005C560F /* ProductUIImageLoaderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 02A275BD23FE57DC005C560F /* ProductUIImageLoaderTests.swift */; }; 02A275C023FE58F6005C560F /* MockImageCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = 02A275BF23FE58F6005C560F /* MockImageCache.swift */; }; @@ -1423,8 +1424,6 @@ 4569317F2653E82B009ED69D /* ShippingLabelCarriers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4569317E2653E82B009ED69D /* ShippingLabelCarriers.swift */; }; 456931842653E9F2009ED69D /* ShippingLabelCarrierRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 456931832653E9F1009ED69D /* ShippingLabelCarrierRow.swift */; }; 45693189265403A1009ED69D /* ShippingLabelCarriersViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45693188265403A1009ED69D /* ShippingLabelCarriersViewModel.swift */; }; - 4569D3C325DC008700CDC3E2 /* SiteAddress.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4569D3C225DC008700CDC3E2 /* SiteAddress.swift */; }; - 4569D3C925DC065B00CDC3E2 /* SiteAddressTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4569D3C825DC065B00CDC3E2 /* SiteAddressTests.swift */; }; 4569D3F425DC1BFF00CDC3E2 /* ShippingLabelFormViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4569D3F325DC1BFF00CDC3E2 /* ShippingLabelFormViewModelTests.swift */; }; 456AB0E7283E610500019CFF /* WCShipInstallTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 456AB0E5283E610500019CFF /* WCShipInstallTableViewCell.swift */; }; 456AB0E8283E610500019CFF /* WCShipInstallTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 456AB0E6283E610500019CFF /* WCShipInstallTableViewCell.xib */; }; @@ -1515,7 +1514,6 @@ 45DB704A26121F3C0064A6CF /* TitleAndValueRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45DB704926121F3C0064A6CF /* TitleAndValueRow.swift */; }; 45DB705A26124C710064A6CF /* TitleAndTextFieldRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45DB705926124C710064A6CF /* TitleAndTextFieldRow.swift */; }; 45DB70602614C7E80064A6CF /* ShippingLabelPackageDetailsResultsControllers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45DB705F2614C7E80064A6CF /* ShippingLabelPackageDetailsResultsControllers.swift */; }; - 45DB70662614CE3F0064A6CF /* Decimal+Helpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45DB70652614CE3F0064A6CF /* Decimal+Helpers.swift */; }; 45DB706C26161F970064A6CF /* DecimalWooTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45DB706B26161F970064A6CF /* DecimalWooTests.swift */; }; 45E3C8F325E7D30300102E84 /* ShippingLabelAddress+Woo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45E3C8F225E7D30300102E84 /* ShippingLabelAddress+Woo.swift */; }; 45E9A6E424DAE1EA00A600E8 /* ProductReviewsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45E9A6E224DAE1EA00A600E8 /* ProductReviewsViewController.swift */; }; @@ -1646,7 +1644,6 @@ 68A38DF52B293B030090C263 /* MockProductListViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 68A38DF42B293B030090C263 /* MockProductListViewModel.swift */; }; 68A5221B2BA1804900A6A584 /* PluginDetailsViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 68A5221A2BA1804900A6A584 /* PluginDetailsViewModelTests.swift */; }; 68A905012ACCFC13004C71D3 /* CollapsibleProductCard.swift in Sources */ = {isa = PBXBuildFile; fileRef = 68A905002ACCFC13004C71D3 /* CollapsibleProductCard.swift */; }; - 68AC9D292ACE598B0042F784 /* ProductImageThumbnail.swift in Sources */ = {isa = PBXBuildFile; fileRef = 68AC9D282ACE598B0042F784 /* ProductImageThumbnail.swift */; }; 68B3BA262D9147480000B2F2 /* AISettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 68B3BA252D9147440000B2F2 /* AISettingsView.swift */; }; 68B681162D9257810098D5CD /* PointOfSaleCouponsController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 68B681152D92577F0098D5CD /* PointOfSaleCouponsController.swift */; }; 68B6F22B2ADE7ED500D171FC /* TooltipView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 68B6F22A2ADE7ED500D171FC /* TooltipView.swift */; }; @@ -1674,7 +1671,6 @@ 68E674AB2A4DAB8C0034BA1E /* CompletedUpgradeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 68E674AA2A4DAB8C0034BA1E /* CompletedUpgradeView.swift */; }; 68E674AD2A4DAC010034BA1E /* CurrentPlanDetailsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 68E674AC2A4DAC010034BA1E /* CurrentPlanDetailsView.swift */; }; 68E674AF2A4DACD50034BA1E /* UpgradeTopBarView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 68E674AE2A4DACD50034BA1E /* UpgradeTopBarView.swift */; }; - 68E952CC287536010095A23D /* SafariView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 68E952CB287536010095A23D /* SafariView.swift */; }; 68E952D0287587BF0095A23D /* CardReaderManualRowView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 68E952CF287587BF0095A23D /* CardReaderManualRowView.swift */; }; 68E952D22875A44B0095A23D /* CardReaderType+Manual.swift in Sources */ = {isa = PBXBuildFile; fileRef = 68E952D12875A44B0095A23D /* CardReaderType+Manual.swift */; }; 68E9F7012E5C499200D45747 /* PointOfSaleSettingsController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 68E9F7002E5C499000D45747 /* PointOfSaleSettingsController.swift */; }; @@ -2915,7 +2911,6 @@ E138D4FC269EEAFE006EA5C6 /* CardPresentPaymentsOnboardingViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = E138D4FB269EEAFE006EA5C6 /* CardPresentPaymentsOnboardingViewModel.swift */; }; E15F163126C5117300D3059B /* InPersonPaymentsNoConnectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E15F163026C5117300D3059B /* InPersonPaymentsNoConnectionView.swift */; }; E15FC74126BC1CED00CF83E6 /* AttributedText.swift in Sources */ = {isa = PBXBuildFile; fileRef = E15FC74026BC1CED00CF83E6 /* AttributedText.swift */; }; - E15FC74326BC1D2700CF83E6 /* SafariSheet.swift in Sources */ = {isa = PBXBuildFile; fileRef = E15FC74226BC1D2700CF83E6 /* SafariSheet.swift */; }; E15FC74526BC213500CF83E6 /* InPersonPaymentsLearnMore.swift in Sources */ = {isa = PBXBuildFile; fileRef = E15FC74426BC213500CF83E6 /* InPersonPaymentsLearnMore.swift */; }; E16058F7285876DE00E471D4 /* LeftImageTitleSubtitleTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = E16058F6285876DE00E471D4 /* LeftImageTitleSubtitleTableViewCell.xib */; }; E16058F9285876E600E471D4 /* LeftImageTitleSubtitleTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = E16058F8285876E600E471D4 /* LeftImageTitleSubtitleTableViewCell.swift */; }; @@ -3744,6 +3739,7 @@ 029F29FB24D94106004751CA /* EditableProductVariationModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditableProductVariationModel.swift; sourceTree = ""; }; 029F29FD24DA5B2D004751CA /* ProductInventorySettingsViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProductInventorySettingsViewModel.swift; sourceTree = ""; }; 029F53172BEB33BC00E31A10 /* CollapsibleCustomerCardAddressView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CollapsibleCustomerCardAddressView.swift; sourceTree = ""; }; + 02A239732E811A690067AB38 /* SiteAddress+ServiceLocator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "SiteAddress+ServiceLocator.swift"; sourceTree = ""; }; 02A275B923FE50AA005C560F /* ProductUIImageLoader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProductUIImageLoader.swift; sourceTree = ""; }; 02A275BD23FE57DC005C560F /* ProductUIImageLoaderTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProductUIImageLoaderTests.swift; sourceTree = ""; }; 02A275BF23FE58F6005C560F /* MockImageCache.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockImageCache.swift; sourceTree = ""; }; @@ -4623,8 +4619,6 @@ 4569317E2653E82B009ED69D /* ShippingLabelCarriers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShippingLabelCarriers.swift; sourceTree = ""; }; 456931832653E9F1009ED69D /* ShippingLabelCarrierRow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShippingLabelCarrierRow.swift; sourceTree = ""; }; 45693188265403A1009ED69D /* ShippingLabelCarriersViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShippingLabelCarriersViewModel.swift; sourceTree = ""; }; - 4569D3C225DC008700CDC3E2 /* SiteAddress.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SiteAddress.swift; sourceTree = ""; }; - 4569D3C825DC065B00CDC3E2 /* SiteAddressTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SiteAddressTests.swift; sourceTree = ""; }; 4569D3F325DC1BFF00CDC3E2 /* ShippingLabelFormViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShippingLabelFormViewModelTests.swift; sourceTree = ""; }; 456AB0E5283E610500019CFF /* WCShipInstallTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WCShipInstallTableViewCell.swift; sourceTree = ""; }; 456AB0E6283E610500019CFF /* WCShipInstallTableViewCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = WCShipInstallTableViewCell.xib; sourceTree = ""; }; @@ -4715,7 +4709,6 @@ 45DB704926121F3C0064A6CF /* TitleAndValueRow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TitleAndValueRow.swift; sourceTree = ""; }; 45DB705926124C710064A6CF /* TitleAndTextFieldRow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TitleAndTextFieldRow.swift; sourceTree = ""; }; 45DB705F2614C7E80064A6CF /* ShippingLabelPackageDetailsResultsControllers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShippingLabelPackageDetailsResultsControllers.swift; sourceTree = ""; }; - 45DB70652614CE3F0064A6CF /* Decimal+Helpers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Decimal+Helpers.swift"; sourceTree = ""; }; 45DB706B26161F970064A6CF /* DecimalWooTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DecimalWooTests.swift; sourceTree = ""; }; 45E3C8F225E7D30300102E84 /* ShippingLabelAddress+Woo.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ShippingLabelAddress+Woo.swift"; sourceTree = ""; }; 45E9A6E224DAE1EA00A600E8 /* ProductReviewsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProductReviewsViewController.swift; sourceTree = ""; }; @@ -4846,7 +4839,6 @@ 68A38DF42B293B030090C263 /* MockProductListViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockProductListViewModel.swift; sourceTree = ""; }; 68A5221A2BA1804900A6A584 /* PluginDetailsViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PluginDetailsViewModelTests.swift; sourceTree = ""; }; 68A905002ACCFC13004C71D3 /* CollapsibleProductCard.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CollapsibleProductCard.swift; sourceTree = ""; }; - 68AC9D282ACE598B0042F784 /* ProductImageThumbnail.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProductImageThumbnail.swift; sourceTree = ""; }; 68B3BA252D9147440000B2F2 /* AISettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AISettingsView.swift; sourceTree = ""; }; 68B681152D92577F0098D5CD /* PointOfSaleCouponsController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PointOfSaleCouponsController.swift; sourceTree = ""; }; 68B6F22A2ADE7ED500D171FC /* TooltipView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TooltipView.swift; sourceTree = ""; }; @@ -4874,7 +4866,6 @@ 68E674AA2A4DAB8C0034BA1E /* CompletedUpgradeView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CompletedUpgradeView.swift; sourceTree = ""; }; 68E674AC2A4DAC010034BA1E /* CurrentPlanDetailsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CurrentPlanDetailsView.swift; sourceTree = ""; }; 68E674AE2A4DACD50034BA1E /* UpgradeTopBarView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UpgradeTopBarView.swift; sourceTree = ""; }; - 68E952CB287536010095A23D /* SafariView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SafariView.swift; sourceTree = ""; }; 68E952CF287587BF0095A23D /* CardReaderManualRowView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CardReaderManualRowView.swift; sourceTree = ""; }; 68E952D12875A44B0095A23D /* CardReaderType+Manual.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CardReaderType+Manual.swift"; sourceTree = ""; }; 68E9F7002E5C499000D45747 /* PointOfSaleSettingsController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PointOfSaleSettingsController.swift; sourceTree = ""; }; @@ -6143,7 +6134,6 @@ E138D4FB269EEAFE006EA5C6 /* CardPresentPaymentsOnboardingViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CardPresentPaymentsOnboardingViewModel.swift; sourceTree = ""; }; E15F163026C5117300D3059B /* InPersonPaymentsNoConnectionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InPersonPaymentsNoConnectionView.swift; sourceTree = ""; }; E15FC74026BC1CED00CF83E6 /* AttributedText.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AttributedText.swift; sourceTree = ""; }; - E15FC74226BC1D2700CF83E6 /* SafariSheet.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SafariSheet.swift; sourceTree = ""; }; E15FC74426BC213500CF83E6 /* InPersonPaymentsLearnMore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InPersonPaymentsLearnMore.swift; sourceTree = ""; }; E16058F6285876DE00E471D4 /* LeftImageTitleSubtitleTableViewCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = LeftImageTitleSubtitleTableViewCell.xib; sourceTree = ""; }; E16058F8285876E600E471D4 /* LeftImageTitleSubtitleTableViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LeftImageTitleSubtitleTableViewCell.swift; sourceTree = ""; }; @@ -9681,7 +9671,6 @@ 45A8DA3F2664E40B00308FBE /* EmptyState.swift */, E10DFC792673595A0083AFF2 /* ShareSheet.swift */, E15FC74026BC1CED00CF83E6 /* AttributedText.swift */, - E15FC74226BC1D2700CF83E6 /* SafariSheet.swift */, E1BAAE9F26BBECEF00F2C037 /* ButtonStyles.swift */, DEFE13C42DF15F36005B3D39 /* ToggleStyles.swift */, DE19BB0B26C2688B00AB70D9 /* SingleSelectionList.swift */, @@ -10680,7 +10669,6 @@ D85136DC231E613900DD0539 /* ReviewsViewModelTests.swift */, D88D5A3C230B5E85007B6E01 /* ServiceLocatorTests.swift */, 45F5A3C223DF31D2007D40E5 /* ShippingInputFormatterTests.swift */, - 4569D3C825DC065B00CDC3E2 /* SiteAddressTests.swift */, B517EA19218B2D2600730EC4 /* StringFormatterTests.swift */, B56BBD15214820A70053A32D /* SyncCoordinatorTests.swift */, 0277AEA4256CAA4200F45C4A /* MockShippingLabel.swift */, @@ -10797,7 +10785,6 @@ 7459A6C521B0680300F83A78 /* RequirementsChecker.swift */, B54FBE542111F70700390F57 /* ResultsController+UIKit.swift */, 74B5713521CD7604008F9B8E /* SharingHelper.swift */, - 4569D3C225DC008700CDC3E2 /* SiteAddress.swift */, 4590B64B261C673B00A6FCE0 /* WeightFormatter.swift */, B5D6DC53214802740003E48A /* SyncCoordinator.swift */, CE22709E2293052700C0626C /* WebviewHelper.swift */, @@ -12154,6 +12141,7 @@ CE1CCB4C20572444000EE3AC /* Extensions */ = { isa = PBXGroup; children = ( + 02A239732E811A690067AB38 /* SiteAddress+ServiceLocator.swift */, 010F7D8C2E7A8447002B02EA /* ProductImageThumbnail+Extensions.swift */, 016582EC2E7897B3001DBB6F /* String+Helpers.swift */, 2D88C1102DF883BD00A6FB2C /* AttributedString+Helpers.swift */, @@ -12173,7 +12161,6 @@ B57C5C9521B80E5400FF82B2 /* Dictionary+Woo.swift */, 740987B221B87760000E4C80 /* FancyAnimatedButton+Woo.swift */, B509FED021C041DF000076A9 /* Locale+Woo.swift */, - 45DB70652614CE3F0064A6CF /* Decimal+Helpers.swift */, B5A56BF4219F5AB20065A902 /* NSNotificationName+Woo.swift */, B57C744420F55BA600EEFC87 /* NSObject+Helpers.swift */, B541B2162189EED4008FE7C1 /* NSMutableAttributedString+Helpers.swift */, @@ -13949,7 +13936,6 @@ E1ABAEF628479E0300F40BB2 /* InPersonPaymentsSelectPluginView.swift */, 684AB8392870677F003DFDD1 /* CardReaderManualsView.swift */, 684AB83B2873DF04003DFDD1 /* CardReaderManualsViewModel.swift */, - 68E952CB287536010095A23D /* SafariView.swift */, 68E952CF287587BF0095A23D /* CardReaderManualRowView.swift */, 68E952D12875A44B0095A23D /* CardReaderType+Manual.swift */, 03EF24FB28BF996F006A033E /* InPersonPaymentsCashOnDeliveryPaymentGatewayHelpers.swift */, @@ -15271,7 +15257,6 @@ CC53FB3C2757EC7200C4CA4F /* ProductSelectorViewModel.swift in Sources */, B65C869A2AA6429700464D5B /* SinglePackageHazmatDeclaration.swift in Sources */, CCA1D5FE293F537400B40560 /* DeltaPercentage.swift in Sources */, - 4569D3C325DC008700CDC3E2 /* SiteAddress.swift in Sources */, 02BBD6E729A268F300243BE2 /* StoreOnboardingViewModel.swift in Sources */, B9B0391828A6838400DC1C83 /* PermanentNoticeView.swift in Sources */, 20BCF6EE2B0E478B00954840 /* WooPaymentsPayoutsOverviewViewModel.swift in Sources */, @@ -15588,7 +15573,6 @@ 027EB57029C062DD003CE551 /* StoreOnboardingLaunchStoreCoordinator.swift in Sources */, 866016512B47F8F800B4047E /* ProductSelector+Blaze.swift in Sources */, 456417F4247D5434001203F6 /* UITableView+Helpers.swift in Sources */, - E15FC74326BC1D2700CF83E6 /* SafariSheet.swift in Sources */, 011D396F2D09FCD200DB1445 /* CardPresentModalLocationRequired.swift in Sources */, 20F7B12D2D12C7B900C08193 /* ItemsContainerState.swift in Sources */, 459DB7D52673721300E2CAD2 /* TopLoaderView.swift in Sources */, @@ -16075,7 +16059,6 @@ 26B119BB24D0B62E00FED5C7 /* SurveyViewController.swift in Sources */, CCD2F51A26D67BB50010E679 /* ShippingLabelServicePackageList.swift in Sources */, 45FDDD65267784AD00ADACE8 /* ShippingLabelSummaryTableViewCell.swift in Sources */, - 45DB70662614CE3F0064A6CF /* Decimal+Helpers.swift in Sources */, 209EEF902C762ED5007969A4 /* POSModalManager.swift in Sources */, 0379C51727BFCE9800A7E284 /* WCPayCardBrand+icons.swift in Sources */, DA1D68C22C36F0980097859A /* PointOfSaleAssets.swift in Sources */, @@ -16564,7 +16547,6 @@ 021125992578D9C20075AD2A /* ShippingLabelPrintingInstructionsView.swift in Sources */, 026826C72BF59E410036F959 /* PointOfSaleCardPresentPaymentScanningForReadersView.swift in Sources */, EEBB81712D8C0839008D6CE5 /* CollapsibleShipmentItemCard.swift in Sources */, - 68E952CC287536010095A23D /* SafariView.swift in Sources */, D449C51C26DE6B5000D75B02 /* IconListItem.swift in Sources */, EE9D03182B89E2B10077CED1 /* OrderStatusEnum+Analytics.swift in Sources */, 014371272DFC8E2800C0279B /* PointOfSaleBarcodeScannerInformationModal.swift in Sources */, @@ -16790,6 +16772,7 @@ 77E53EC52510C193003D385F /* ProductDownloadListViewController+Droppable.swift in Sources */, 01BB6C0A2D09E9630094D55B /* LocationService.swift in Sources */, 3F50FE4328CAEBA800C89201 /* AppLocalizedString.swift in Sources */, + 02A239742E811A920067AB38 /* SiteAddress+ServiceLocator.swift in Sources */, 0259D5F92581F0E6003B1CD6 /* ShippingLabelPaperSizeOptionView.swift in Sources */, D81F2D37225F0D160084BF9C /* EmptyListMessageWithActionView.swift in Sources */, B99B87A72AEFCF0A006B8AB1 /* Order+Empty.swift in Sources */, @@ -17877,7 +17860,6 @@ 02EFF81A2ABC28BA0015ABB2 /* GiftCardInputViewModelTests.swift in Sources */, B9CB14E02A42E246005912C2 /* BarcodeScannerErrorNoticeFactoryTests.swift in Sources */, 6856D49DB7DCF4D87745C0B1 /* MockPushNotificationsManager.swift in Sources */, - 4569D3C925DC065B00CDC3E2 /* SiteAddressTests.swift in Sources */, 207CEA882E1FD6F80023EC35 /* PointOfSaleBarcodeScannerSetupScanTesterTests.swift in Sources */, EE289AFE2C9D9CF0004AB1A6 /* ProductCreationAIEligibilityCheckerTests.swift in Sources */, CC4D1E7925EE415D00B6E4E7 /* RenameAttributesViewModelTests.swift in Sources */,