From a9f8d153de4652b822beac77d2162d66ee076472 Mon Sep 17 00:00:00 2001 From: Matias Bzurovski Date: Mon, 7 Oct 2024 16:11:28 +0200 Subject: [PATCH 1/5] fix issue with duplicate deposit status --- RadixWallet.xcodeproj/project.pbxproj | 4 +++ .../Asset/DepositStatus.swift | 23 ++++++++++++++ .../Asset/ResourceAsset+Reducer.swift | 11 ------- .../Asset/ResourceAsset+View.swift | 2 +- .../ReceivingAccount+Reducer.swift | 10 +++---- .../TransferAccountList+Reducer.swift | 30 +++++++++++-------- 6 files changed, 49 insertions(+), 31 deletions(-) create mode 100644 RadixWallet/Features/AssetTransferFeature/Components/TransferAccountList/ReceivingAccount/Asset/DepositStatus.swift diff --git a/RadixWallet.xcodeproj/project.pbxproj b/RadixWallet.xcodeproj/project.pbxproj index 5e818e9d21..8b166663aa 100644 --- a/RadixWallet.xcodeproj/project.pbxproj +++ b/RadixWallet.xcodeproj/project.pbxproj @@ -734,6 +734,7 @@ 5B45E2FF2BC59780007C4C84 /* SignWithFactorSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B45E2FD2BC59780007C4C84 /* SignWithFactorSource.swift */; }; 5B45E3012BC5A491007C4C84 /* FactorSourceAccess+ViewState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B45E3002BC5A491007C4C84 /* FactorSourceAccess+ViewState.swift */; }; 5B4712CC2C526146003B4712 /* HeaderButtonStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B4712CB2C526146003B4712 /* HeaderButtonStyle.swift */; }; + 5B4E1D132CB421EB002FAC2E /* DepositStatus.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B4E1D122CB421E3002FAC2E /* DepositStatus.swift */; }; 5B526ADB2C876E7C00AF8B72 /* AccountLockerClaimDetails.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B526ADA2C876E7C00AF8B72 /* AccountLockerClaimDetails.swift */; }; 5B526AEC2C89C3C200AF8B72 /* ResourcesVisibilityClient+Interface.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B526AE82C89C3C200AF8B72 /* ResourcesVisibilityClient+Interface.swift */; }; 5B526AED2C89C3C200AF8B72 /* ResourcesVisibilityClient+Live.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B526AE92C89C3C200AF8B72 /* ResourcesVisibilityClient+Live.swift */; }; @@ -1946,6 +1947,7 @@ 5B45E2FD2BC59780007C4C84 /* SignWithFactorSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SignWithFactorSource.swift; sourceTree = ""; }; 5B45E3002BC5A491007C4C84 /* FactorSourceAccess+ViewState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "FactorSourceAccess+ViewState.swift"; sourceTree = ""; }; 5B4712CB2C526146003B4712 /* HeaderButtonStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HeaderButtonStyle.swift; sourceTree = ""; }; + 5B4E1D122CB421E3002FAC2E /* DepositStatus.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DepositStatus.swift; sourceTree = ""; }; 5B526ADA2C876E7C00AF8B72 /* AccountLockerClaimDetails.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountLockerClaimDetails.swift; sourceTree = ""; }; 5B526AE82C89C3C200AF8B72 /* ResourcesVisibilityClient+Interface.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "ResourcesVisibilityClient+Interface.swift"; sourceTree = ""; }; 5B526AE92C89C3C200AF8B72 /* ResourcesVisibilityClient+Live.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "ResourcesVisibilityClient+Live.swift"; sourceTree = ""; }; @@ -3349,6 +3351,7 @@ 48CFBD402ADC10D800E77A5C /* Asset */ = { isa = PBXGroup; children = ( + 5B4E1D122CB421E3002FAC2E /* DepositStatus.swift */, 48CFBD412ADC10D800E77A5C /* ResourceAsset+Reducer.swift */, 48CFBD442ADC10D800E77A5C /* ResourceAsset+View.swift */, 48CFBD452ADC10D800E77A5C /* FungibleResourceAsset+Reducer.swift */, @@ -7690,6 +7693,7 @@ 48CFC3762ADC10D900E77A5C /* ValidatorStakeView.swift in Sources */, 48CFC4612ADC10DA00E77A5C /* MakeTransactionHeaderInput.swift in Sources */, A40815C62C7E0D08005E65B9 /* NonFungibleResourcesCollectionItemVaultAggregatedVault.swift in Sources */, + 5B4E1D132CB421EB002FAC2E /* DepositStatus.swift in Sources */, 48CFC2F22ADC10D900E77A5C /* CreatePersonaCoordinator+View.swift in Sources */, 48CFC4652ADC10DA00E77A5C /* AccountPortfoliosClient+Live.swift in Sources */, 48CFC5832ADC10DA00E77A5C /* SecureStorageClient+Test.swift in Sources */, diff --git a/RadixWallet/Features/AssetTransferFeature/Components/TransferAccountList/ReceivingAccount/Asset/DepositStatus.swift b/RadixWallet/Features/AssetTransferFeature/Components/TransferAccountList/ReceivingAccount/Asset/DepositStatus.swift new file mode 100644 index 0000000000..a02c412d2c --- /dev/null +++ b/RadixWallet/Features/AssetTransferFeature/Components/TransferAccountList/ReceivingAccount/Asset/DepositStatus.swift @@ -0,0 +1,23 @@ +import Foundation + +public typealias DepositStatus = DepositStatusPerResource.DepositStatus +public typealias DepositStatusPerResources = IdentifiedArrayOf + +// MARK: - DepositStatusPerResource +public struct DepositStatusPerResource: Sendable, Hashable, Identifiable { + let resourceAddress: ResourceAddress + let depositStatus: DepositStatus + + public var id: ResourceAddress { resourceAddress } + + public enum DepositStatus: Sendable, Hashable { + /// The deposit of this asset is allowed. + case allowed + + /// The user needs to provide an additional signature to deposit this asset. + case additionalSignatureRequired + + /// The user cannot deposit this asset since the receiving acccount has disallowed it. + case denied + } +} diff --git a/RadixWallet/Features/AssetTransferFeature/Components/TransferAccountList/ReceivingAccount/Asset/ResourceAsset+Reducer.swift b/RadixWallet/Features/AssetTransferFeature/Components/TransferAccountList/ReceivingAccount/Asset/ResourceAsset+Reducer.swift index 8c26da886c..238ed5f34a 100644 --- a/RadixWallet/Features/AssetTransferFeature/Components/TransferAccountList/ReceivingAccount/Asset/ResourceAsset+Reducer.swift +++ b/RadixWallet/Features/AssetTransferFeature/Components/TransferAccountList/ReceivingAccount/Asset/ResourceAsset+Reducer.swift @@ -247,17 +247,6 @@ extension OnLedgerEntity.NonFungibleToken { // MARK: - ResourceAsset.State.DepositStatus extension ResourceAsset.State { - public enum DepositStatus: Sendable, Hashable { - /// The deposit of this asset is allowed. - case allowed - - /// The user needs to provide an additional signature to deposit this asset. - case additionalSignatureRequired - - /// The user cannot deposit this asset since the receiving acccount has disallowed it. - case denied - } - var isDepositEnabled: Bool { switch depositStatus { case .idle, .loading: diff --git a/RadixWallet/Features/AssetTransferFeature/Components/TransferAccountList/ReceivingAccount/Asset/ResourceAsset+View.swift b/RadixWallet/Features/AssetTransferFeature/Components/TransferAccountList/ReceivingAccount/Asset/ResourceAsset+View.swift index f859b0548f..6d0a141a89 100644 --- a/RadixWallet/Features/AssetTransferFeature/Components/TransferAccountList/ReceivingAccount/Asset/ResourceAsset+View.swift +++ b/RadixWallet/Features/AssetTransferFeature/Components/TransferAccountList/ReceivingAccount/Asset/ResourceAsset+View.swift @@ -111,7 +111,7 @@ private extension View { } } -private extension Loadable { +private extension Loadable { var hint: Hint.ViewState? { switch self { case .idle, .loading, .failure, .success(.allowed): diff --git a/RadixWallet/Features/AssetTransferFeature/Components/TransferAccountList/ReceivingAccount/ReceivingAccount+Reducer.swift b/RadixWallet/Features/AssetTransferFeature/Components/TransferAccountList/ReceivingAccount/ReceivingAccount+Reducer.swift index c71f691d55..dac1be8ab0 100644 --- a/RadixWallet/Features/AssetTransferFeature/Components/TransferAccountList/ReceivingAccount/ReceivingAccount+Reducer.swift +++ b/RadixWallet/Features/AssetTransferFeature/Components/TransferAccountList/ReceivingAccount/ReceivingAccount+Reducer.swift @@ -2,8 +2,6 @@ import ComposableArchitecture import Sargon import SwiftUI -public typealias AssetsDepositStatus = [ResourceAddress: ResourceAsset.State.DepositStatus] - // MARK: - ReceivingAccount public struct ReceivingAccount: Sendable, FeatureReducer { public struct State: Sendable, Hashable, Identifiable { @@ -84,16 +82,16 @@ extension ReceivingAccount.State { assets.contains(where: { $0.depositStatus == .loading }) } - mutating func setAllDepositStatus(_ status: Loadable) { + mutating func setAllDepositStatus(_ status: Loadable) { assets.mutateAll { asset in asset.depositStatus = status } } - mutating func updateDepositStatus(values: AssetsDepositStatus) { + mutating func updateDepositStatus(values: DepositStatusPerResources) { assets.mutateAll { asset in - if let depositStatus = values[asset.resourceAddress] { - asset.depositStatus = .success(depositStatus) + if let value = values[id: asset.resourceAddress] { + asset.depositStatus = .success(value.depositStatus) } } } diff --git a/RadixWallet/Features/AssetTransferFeature/Components/TransferAccountList/TransferAccountList+Reducer.swift b/RadixWallet/Features/AssetTransferFeature/Components/TransferAccountList/TransferAccountList+Reducer.swift index 655d96d641..fab95fb83a 100644 --- a/RadixWallet/Features/AssetTransferFeature/Components/TransferAccountList/TransferAccountList+Reducer.swift +++ b/RadixWallet/Features/AssetTransferFeature/Components/TransferAccountList/TransferAccountList+Reducer.swift @@ -44,8 +44,8 @@ public struct TransferAccountList: Sendable, FeatureReducer { } public enum InternalAction: Equatable, Sendable { - case setAllDepositStatus(accountId: ReceivingAccount.State.ID, status: Loadable) - case setDepositStatus(accountId: ReceivingAccount.State.ID, values: AssetsDepositStatus) + case setAllDepositStatus(accountId: ReceivingAccount.State.ID, status: Loadable) + case setDepositStatus(accountId: ReceivingAccount.State.ID, values: DepositStatusPerResources) } public struct Destination: DestinationReducer { @@ -315,27 +315,31 @@ private extension TransferAccountList { } } - typealias DepositStatus = ResourceAsset.State.DepositStatus - - func getStatusesForProfileAccount(_ account: Account, assets: IdentifiedArrayOf) async -> AssetsDepositStatus { - let result = await assets.parallelMap { asset in + func getStatusesForProfileAccount(_ account: Account, assets: IdentifiedArrayOf) async -> DepositStatusPerResources { + await assets.parallelMap { asset in let result = await needsSignatureForDepositting(into: account, resource: asset.resourceAddress) - return (asset.resourceAddress, result ? DepositStatus.additionalSignatureRequired : .allowed) + return DepositStatusPerResource( + resourceAddress: asset.resourceAddress, + depositStatus: result ? .additionalSignatureRequired : .allowed + ) } - let values = Dictionary(uniqueKeysWithValues: result.map { ($0.0, $0.1) }) - return values + .asIdentified() } - func getStatusesForExternalAccount(_ account: AccountAddress, resourceAddresses: [ResourceAddress]) async throws -> AssetsDepositStatus { + func getStatusesForExternalAccount(_ account: AccountAddress, resourceAddresses: [ResourceAddress]) async throws -> DepositStatusPerResources { let result = try await gatewayAPIClient.prevalidateDeposit(.init(accountAddress: account.address, resourceAddresses: resourceAddresses.map(\.address))) if let behavior = result.resourceSpecificBehaviour { - return Dictionary(uniqueKeysWithValues: behavior.compactMap { item in + return behavior.compactMap { item -> DepositStatusPerResource? in guard let address = try? ResourceAddress(validatingAddress: item.resourceAddress) else { return nil } - return (address, item.allowsTryDeposit ? .allowed : .denied) - }) + return DepositStatusPerResource( + resourceAddress: address, + depositStatus: item.allowsTryDeposit ? .allowed : .denied + ) + } + .asIdentified() } else { return .init() } From 85a92735cd749502dd34339e7468261b24b382c2 Mon Sep 17 00:00:00 2001 From: Matias Bzurovski Date: Mon, 7 Oct 2024 16:11:38 +0200 Subject: [PATCH 2/5] remove warning --- RadixWallet/Prelude/Extensions/Array+Identifiable.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/RadixWallet/Prelude/Extensions/Array+Identifiable.swift b/RadixWallet/Prelude/Extensions/Array+Identifiable.swift index 261ad3dfd2..58d8c99a51 100644 --- a/RadixWallet/Prelude/Extensions/Array+Identifiable.swift +++ b/RadixWallet/Prelude/Extensions/Array+Identifiable.swift @@ -6,7 +6,7 @@ extension Array where Element: Identifiable { public func asIdentified() -> IdentifiedArrayOf { var array: IdentifiedArrayOf = [] for element in self { - let (inserted, _) = array.append(element) + let _ = array.append(element) } return array } From eb920601ec70cdbfd8b34635f7815e3defab155b Mon Sep 17 00:00:00 2001 From: Matias Bzurovski Date: Mon, 7 Oct 2024 16:23:02 +0200 Subject: [PATCH 3/5] Add custom linter script phase --- RadixWallet.xcodeproj/project.pbxproj | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/RadixWallet.xcodeproj/project.pbxproj b/RadixWallet.xcodeproj/project.pbxproj index 8b166663aa..58fab228b5 100644 --- a/RadixWallet.xcodeproj/project.pbxproj +++ b/RadixWallet.xcodeproj/project.pbxproj @@ -6822,6 +6822,7 @@ buildConfigurationList = 48CFBC5D2ADC106400E77A5C /* Build configuration list for PBXNativeTarget "RadixWallet" */; buildPhases = ( 5B9846C32BBD643700E814F3 /* Create SensitiveInfo Sample */, + 5B4E1D142CB423C9002FAC2E /* Custom linter */, 48CFBC4B2ADC106300E77A5C /* Sources */, 48CFBC4C2ADC106300E77A5C /* Frameworks */, 48CFBC4D2ADC106300E77A5C /* Resources */, @@ -7009,6 +7010,24 @@ /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ + 5B4E1D142CB423C9002FAC2E /* Custom linter */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + ); + name = "Custom linter"; + outputFileListPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "# Custom linter to avoid using Dictionary.init(uniqueKeysWithValues:)\n# Quick solution before considering using an external tool, such as SwiftLint \n\nPATTERN=\"uniqueKeysWithValues:\"\nFILES=$(grep -rnw --include=\\*.swift \"$PATTERN\" \"$SRCROOT\")\n\nif [ ! -z \"$FILES\" ]; then\n echo \"Error: We shouldn't use Dictionary(uniqueKeysWithValues:), as it could lead to unhandled crashes if key is repeated\"\n echo \"Instead, use our custom init(keysWithValues:), or consider refactoring your solution to use IdentifiedArrayOf\"\n echo \"Please fix on the following files:\"\n echo \"$FILES\"\n exit 1 \nfi\n"; + }; 5B9846C32BBD643700E814F3 /* Create SensitiveInfo Sample */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; From 6f63b688c4dfcf9968104d262f174de643b76dd9 Mon Sep 17 00:00:00 2001 From: Matias Bzurovski Date: Mon, 7 Oct 2024 16:47:38 +0200 Subject: [PATCH 4/5] Use throwing init rather than Dictionary default --- .../AccountLockersClient+Live.swift | 2 +- .../FactorSourcesClient+Live.swift | 4 +-- RadixWallet/Core/Dictionary+Extensions.swift | 33 +++++++++++++++++++ .../TransactionReview+Sections.swift | 12 +++---- 4 files changed, 42 insertions(+), 9 deletions(-) diff --git a/RadixWallet/Clients/AccountLockersClient/AccountLockersClient+Live.swift b/RadixWallet/Clients/AccountLockersClient/AccountLockersClient+Live.swift index 6f124c5b7d..059be212f7 100644 --- a/RadixWallet/Clients/AccountLockersClient/AccountLockersClient+Live.swift +++ b/RadixWallet/Clients/AccountLockersClient/AccountLockersClient+Live.swift @@ -166,7 +166,7 @@ extension AccountLockersClient { let response = try await gatewayAPIClient.getAccountLockerTouchedAt(.init(accountLockers: accountLockers)) return (accountAddress, response) } - return Dictionary(uniqueKeysWithValues: result.map { ($0.0, $0.1) }) + return try Dictionary(keysWithValues: result.map { ($0.0, $0.1) }) } @Sendable diff --git a/RadixWallet/Clients/FactorSourcesClient/FactorSourcesClient+Live.swift b/RadixWallet/Clients/FactorSourcesClient/FactorSourcesClient+Live.swift index 4edaa74790..a1e6452e61 100644 --- a/RadixWallet/Clients/FactorSourcesClient/FactorSourcesClient+Live.swift +++ b/RadixWallet/Clients/FactorSourcesClient/FactorSourcesClient+Live.swift @@ -401,8 +401,8 @@ func signingFactors( } } - return SigningFactors( - uniqueKeysWithValues: signingFactors.map { keyValuePair -> (key: FactorSourceKind, value: NonEmpty>) in + return try SigningFactors( + keysWithValues: signingFactors.map { keyValuePair -> (key: FactorSourceKind, value: NonEmpty>) in assert(!keyValuePair.value.isEmpty, "Incorrect implementation, IdentifiedArrayOf should never be empty.") let value: NonEmpty> = .init(rawValue: Set(keyValuePair.value))! return (key: keyValuePair.key, value: value) diff --git a/RadixWallet/Core/Dictionary+Extensions.swift b/RadixWallet/Core/Dictionary+Extensions.swift index 309994a219..0613a0ade0 100644 --- a/RadixWallet/Core/Dictionary+Extensions.swift +++ b/RadixWallet/Core/Dictionary+Extensions.swift @@ -14,3 +14,36 @@ extension Dictionary { ) } } + +extension Dictionary { + // Custom initializer that throws if there are duplicate keys + init(keysWithValues: [(Key, Value)]) throws { + self = [:] // Initialize an empty dictionary + + for (key, value) in keysWithValues { + if self[key] != nil { + throw DictionaryDuplicateKeyError() + } + self[key] = value + } + } + + struct DictionaryDuplicateKeyError: Error {} +} + +extension OrderedDictionary { + // Custom initializer that throws if there are duplicate keys + init(keysWithValues: [(Key, Value)]) throws { + self = OrderedDictionary() + + for (key, value) in keysWithValues { + // Check if the key already exists in the OrderedDictionary + if self[key] != nil { + throw OrderedDictionaryDuplicateKeyError() + } + self[key] = value + } + } + + struct OrderedDictionaryDuplicateKeyError: Error {} +} diff --git a/RadixWallet/Features/TransactionReviewFeature/TransactionReview+Sections.swift b/RadixWallet/Features/TransactionReviewFeature/TransactionReview+Sections.swift index f313e32937..0164d98eef 100644 --- a/RadixWallet/Features/TransactionReviewFeature/TransactionReview+Sections.swift +++ b/RadixWallet/Features/TransactionReviewFeature/TransactionReview+Sections.swift @@ -33,8 +33,8 @@ extension TransactionReview { let allAddresses: IdentifiedArrayOf = Array((allWithdrawAddresses + allDepositAddresses).uniqued()).asIdentified() func resourcesInfo(_ resourceAddresses: [ResourceAddress]) async throws -> ResourcesInfo { - var newlyCreatedMetadata = Dictionary( - uniqueKeysWithValues: resourceAddresses.compactMap { resourceAddress in + var newlyCreatedMetadata = try Dictionary( + keysWithValues: resourceAddresses.compactMap { resourceAddress in summary.newEntities.metadata[resourceAddress].map { ( resourceAddress, @@ -114,7 +114,7 @@ extension TransactionReview { let dApps = await extractDappEntities(poolAddresses.map(\.asGeneral)) - let perPoolUnitDapps = perPoolUnitDapps(dApps, poolInteractions: poolContributions) + let perPoolUnitDapps = try perPoolUnitDapps(dApps, poolInteractions: poolContributions) // Extract Contributing to Pools section let pools: TransactionReviewPools.State? = try await extractDapps(dApps, unknownTitle: L10n.TransactionReview.unknownPools) @@ -155,7 +155,7 @@ extension TransactionReview { let dApps = await extractDappEntities(poolAddresses.map(\.asGeneral)) - let perPoolUnitDapps = perPoolUnitDapps(dApps, poolInteractions: poolRedemptions) + let perPoolUnitDapps = try perPoolUnitDapps(dApps, poolInteractions: poolRedemptions) // Extract Contributing to Pools section let pools: TransactionReviewPools.State? = try await extractDapps(dApps, unknownTitle: L10n.TransactionReview.unknownPools) @@ -577,8 +577,8 @@ extension TransactionReview { private func perPoolUnitDapps( _ dappEntities: [(address: Address, entity: TransactionReview.DappEntity?)], poolInteractions: [some TrackedPoolInteraction] - ) -> ResourceAssociatedDapps { - Dictionary(uniqueKeysWithValues: dappEntities.compactMap { data -> (ResourceAddress, OnLedgerEntity.Metadata)? in + ) throws -> ResourceAssociatedDapps { + try Dictionary(keysWithValues: dappEntities.compactMap { data -> (ResourceAddress, OnLedgerEntity.Metadata)? in let poolUnitResource: ResourceAddress? = poolInteractions .first(where: { $0.poolAddress.asGeneral == data.address })? .poolUnitsResourceAddress From 4998c2a92346855898264c2d17865a83517daf55 Mon Sep 17 00:00:00 2001 From: Matias Bzurovski Date: Mon, 7 Oct 2024 16:54:43 +0200 Subject: [PATCH 5/5] lint --- RadixWallet/Core/Dictionary+Extensions.swift | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/RadixWallet/Core/Dictionary+Extensions.swift b/RadixWallet/Core/Dictionary+Extensions.swift index 0613a0ade0..45112c650b 100644 --- a/RadixWallet/Core/Dictionary+Extensions.swift +++ b/RadixWallet/Core/Dictionary+Extensions.swift @@ -18,7 +18,7 @@ extension Dictionary { extension Dictionary { // Custom initializer that throws if there are duplicate keys init(keysWithValues: [(Key, Value)]) throws { - self = [:] // Initialize an empty dictionary + self = [:] for (key, value) in keysWithValues { if self[key] != nil { @@ -37,7 +37,6 @@ extension OrderedDictionary { self = OrderedDictionary() for (key, value) in keysWithValues { - // Check if the key already exists in the OrderedDictionary if self[key] != nil { throw OrderedDictionaryDuplicateKeyError() }