diff --git a/App/iOS/App.swift b/App/iOS/App.swift index b1888f30..48c8ca8b 100644 --- a/App/iOS/App.swift +++ b/App/iOS/App.swift @@ -65,8 +65,8 @@ struct IsowordsApp: App { WindowGroup { AppView(store: self.appDelegate.store) } - .onChange(of: self.scenePhase) { - self.appDelegate.store.send(.didChangeScenePhase($0)) + .onChange(of: self.scenePhase) { _, newPhase in + self.appDelegate.store.send(.didChangeScenePhase(newPhase)) } } } diff --git a/App/isowords.xcodeproj/project.pbxproj b/App/isowords.xcodeproj/project.pbxproj index 20b2baec..94678a2d 100644 --- a/App/isowords.xcodeproj/project.pbxproj +++ b/App/isowords.xcodeproj/project.pbxproj @@ -1339,7 +1339,6 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 16.0; MACOSX_DEPLOYMENT_TARGET = 10.15; MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; @@ -1826,7 +1825,6 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 16.0; MACOSX_DEPLOYMENT_TARGET = 10.15; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; @@ -1884,7 +1882,6 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 16.0; MACOSX_DEPLOYMENT_TARGET = 10.15; MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; diff --git a/App/isowords.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/App/isowords.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index 58b2bdd9..9fa17dfa 100644 --- a/App/isowords.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/App/isowords.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -86,8 +86,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/pointfreeco/swift-case-paths", "state" : { - "revision" : "ed7facdd4a361514b46e3bbc6238cd41c84be4ec", - "version" : "1.1.1" + "revision" : "a5521dde99570789d8cb7c43e51418d7cd1a87ca", + "version" : "1.1.2" } }, { @@ -95,8 +95,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/pointfreeco/swift-clocks", "state" : { - "revision" : "d1fd837326aa719bee979bdde1f53cd5797443eb", - "version" : "1.0.0" + "revision" : "a8421d68068d8f45fbceb418fbf22c5dad4afd33", + "version" : "1.0.2" } }, { @@ -113,8 +113,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/pointfreeco/swift-composable-architecture", "state" : { - "branch" : "main", - "revision" : "af5ae21f65553d5bb39d55d9b4f80182a0c4fcb5" + "revision" : "d8f81ca2d43026641c5c60729d01f245ccc32370", + "version" : "1.5.3" } }, { @@ -140,8 +140,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/pointfreeco/swift-custom-dump", "state" : { - "revision" : "65fc9e2b62727cacfab9fc60d580c284a4b9308c", - "version" : "1.1.1" + "revision" : "aedcf6f4cd486ccef5b312ccac85d4b3f6e58605", + "version" : "1.1.2" } }, { @@ -149,8 +149,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/pointfreeco/swift-dependencies", "state" : { - "revision" : "63301f4a181ed9aefb46dccef2dfb66466798341", - "version" : "1.1.1" + "revision" : "9783b58167f7618cb86011156e741cbc6f4cc864", + "version" : "1.1.2" } }, { @@ -176,8 +176,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/apple/swift-http-types", "state" : { - "revision" : "99d066e29effa8845e4761dd3f2f831edfdf8925", - "version" : "1.0.0" + "revision" : "1827dc94bdab2eb5f2fc804e9b0cb43574282566", + "version" : "1.0.2" } }, { @@ -293,8 +293,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/pointfreeco/swift-snapshot-testing", "state" : { - "revision" : "4862d48562483d274a2ac7522d905c9237a31a48", - "version" : "1.15.0" + "revision" : "59b663f68e69f27a87b45de48cb63264b8194605", + "version" : "1.15.1" } }, { diff --git a/Package.swift b/Package.swift index dab77533..d41a3851 100644 --- a/Package.swift +++ b/Package.swift @@ -1,4 +1,4 @@ -// swift-tools-version:5.7 +// swift-tools-version:5.9 import Foundation import PackageDescription @@ -7,10 +7,10 @@ import PackageDescription var package = Package( name: "isowords", platforms: [ - .iOS(.v16), - .macOS(.v13), - .tvOS(.v16), - .watchOS(.v9), + .iOS(.v17), + .macOS(.v14), + .tvOS(.v17), + .watchOS(.v10), ], products: [ .library(name: "Build", targets: ["Build"]), @@ -28,11 +28,9 @@ var package = Package( dependencies: [ .package(url: "https://github.com/apple/swift-crypto", from: "1.1.6"), .package(url: "https://github.com/pointfreeco/swift-case-paths", from: "1.1.0"), - .package( - url: "https://github.com/pointfreeco/swift-composable-architecture", branch: "main" - ), + .package(url: "https://github.com/pointfreeco/swift-composable-architecture", from: "1.5.3" ), .package(url: "https://github.com/pointfreeco/swift-custom-dump", from: "1.0.0"), - .package(url: "https://github.com/pointfreeco/swift-dependencies", from: "1.0.0"), + .package(url: "https://github.com/pointfreeco/swift-dependencies", from: "1.1.0"), .package(url: "https://github.com/pointfreeco/swift-gen", from: "0.3.0"), .package(url: "https://github.com/pointfreeco/swift-parsing", from: "0.12.0"), .package(url: "https://github.com/pointfreeco/swift-tagged", from: "0.6.0"), @@ -45,7 +43,8 @@ var package = Package( .target( name: "Build", dependencies: [ - .product(name: "Dependencies", package: "swift-composable-architecture"), + .product(name: "Dependencies", package: "swift-dependencies"), + .product(name: "DependenciesMacros", package: "swift-dependencies"), .product(name: "Tagged", package: "swift-tagged"), .product(name: "XCTestDynamicOverlay", package: "xctest-dynamic-overlay"), ] @@ -61,7 +60,8 @@ var package = Package( name: "DictionaryClient", dependencies: [ "SharedModels", - .product(name: "Dependencies", package: "swift-composable-architecture"), + .product(name: "Dependencies", package: "swift-dependencies"), + .product(name: "DependenciesMacros", package: "swift-dependencies"), .product(name: "XCTestDynamicOverlay", package: "xctest-dynamic-overlay"), ] ), @@ -254,7 +254,8 @@ if ProcessInfo.processInfo.environment["TEST_SERVER"] == nil { "SharedModels", "XCTestDebugSupport", .product(name: "CasePaths", package: "swift-case-paths"), - .product(name: "Dependencies", package: "swift-composable-architecture"), + .product(name: "Dependencies", package: "swift-dependencies"), + .product(name: "DependenciesMacros", package: "swift-dependencies"), .product(name: "XCTestDynamicOverlay", package: "xctest-dynamic-overlay"), ] ), @@ -265,7 +266,7 @@ if ProcessInfo.processInfo.environment["TEST_SERVER"] == nil { "ServerRouter", "SharedModels", "TcaHelpers", - .product(name: "Dependencies", package: "swift-composable-architecture"), + .product(name: "Dependencies", package: "swift-dependencies"), ], exclude: ["Secrets.swift.example"] ), @@ -525,7 +526,8 @@ if ProcessInfo.processInfo.environment["TEST_SERVER"] == nil { .target( name: "DeviceId", dependencies: [ - .product(name: "Dependencies", package: "swift-composable-architecture"), + .product(name: "Dependencies", package: "swift-dependencies"), + .product(name: "DependenciesMacros", package: "swift-dependencies"), .product(name: "XCTestDynamicOverlay", package: "xctest-dynamic-overlay"), ] ), @@ -935,7 +937,6 @@ if ProcessInfo.processInfo.environment["TEST_SERVER"] == nil { name: "UIApplicationClient", dependencies: [ .product(name: "ComposableArchitecture", package: "swift-composable-architecture"), - .product(name: "XCTestDynamicOverlay", package: "xctest-dynamic-overlay"), ] ), .target( @@ -1301,9 +1302,9 @@ package.targets.append(contentsOf: [ "ShareGameMiddleware", "SnsClient", "VerifyReceiptMiddleware", + .product(name: "DependenciesMacros", package: "swift-dependencies"), .product(name: "HttpPipeline", package: "swift-web"), .product(name: "Overture", package: "swift-overture"), - .product(name: "XCTestDynamicOverlay", package: "xctest-dynamic-overlay"), ], resources: [.process("Resources/")] ), diff --git a/Sources/ApiClient/Client.swift b/Sources/ApiClient/Client.swift index c91bda2b..80d3d50c 100644 --- a/Sources/ApiClient/Client.swift +++ b/Sources/ApiClient/Client.swift @@ -1,38 +1,19 @@ +import DependenciesMacros import Foundation import SharedModels +@DependencyClient public struct ApiClient { public var apiRequest: @Sendable (ServerRoute.Api.Route) async throws -> (Data, URLResponse) public var authenticate: @Sendable (ServerRoute.AuthenticateRequest) async throws -> CurrentPlayerEnvelope - public var baseUrl: @Sendable () -> URL + public var baseUrl: @Sendable () -> URL = { URL(string: "/")! } public var currentPlayer: @Sendable () -> CurrentPlayerEnvelope? public var logout: @Sendable () async -> Void public var refreshCurrentPlayer: @Sendable () async throws -> CurrentPlayerEnvelope public var request: @Sendable (ServerRoute) async throws -> (Data, URLResponse) public var setBaseUrl: @Sendable (URL) async -> Void - public init( - apiRequest: @escaping @Sendable (ServerRoute.Api.Route) async throws -> (Data, URLResponse), - authenticate: @escaping @Sendable (ServerRoute.AuthenticateRequest) async throws -> - CurrentPlayerEnvelope, - baseUrl: @escaping @Sendable () -> URL, - currentPlayer: @escaping @Sendable () -> CurrentPlayerEnvelope?, - logout: @escaping @Sendable () async -> Void, - refreshCurrentPlayer: @escaping @Sendable () async throws -> CurrentPlayerEnvelope, - request: @escaping @Sendable (ServerRoute) async throws -> (Data, URLResponse), - setBaseUrl: @escaping @Sendable (URL) async -> Void - ) { - self.apiRequest = apiRequest - self.authenticate = authenticate - self.baseUrl = baseUrl - self.currentPlayer = currentPlayer - self.logout = logout - self.refreshCurrentPlayer = refreshCurrentPlayer - self.request = request - self.setBaseUrl = setBaseUrl - } - public func apiRequest( route: ServerRoute.Api.Route, file: StaticString = #file, diff --git a/Sources/ApiClient/TestKey.swift b/Sources/ApiClient/TestKey.swift index a724e0cf..2e4d2170 100644 --- a/Sources/ApiClient/TestKey.swift +++ b/Sources/ApiClient/TestKey.swift @@ -3,7 +3,6 @@ import Dependencies import Foundation import SharedModels import XCTestDebugSupport -import XCTestDynamicOverlay extension DependencyValues { public var apiClient: ApiClient { @@ -14,17 +13,7 @@ extension DependencyValues { extension ApiClient: TestDependencyKey { public static let previewValue = Self.noop - - public static let testValue = Self( - apiRequest: unimplemented("\(Self.self).apiRequest"), - authenticate: unimplemented("\(Self.self).authenticate"), - baseUrl: unimplemented("\(Self.self).baseUrl", placeholder: URL(string: "/")!), - currentPlayer: unimplemented("\(Self.self).currentPlayer"), - logout: unimplemented("\(Self.self).logout"), - refreshCurrentPlayer: unimplemented("\(Self.self).refreshCurrentPlayer"), - request: unimplemented("\(Self.self).request"), - setBaseUrl: unimplemented("\(Self.self).setBaseUrl") - ) + public static let testValue = Self() } extension ApiClient { diff --git a/Sources/AudioPlayerClient/Client.swift b/Sources/AudioPlayerClient/Client.swift index 77b1bebd..48ac1f56 100644 --- a/Sources/AudioPlayerClient/Client.swift +++ b/Sources/AudioPlayerClient/Client.swift @@ -1,8 +1,11 @@ +import DependenciesMacros + +@DependencyClient public struct AudioPlayerClient { public var load: @Sendable ([Sound]) async -> Void public var loop: @Sendable (Sound) async -> Void public var play: @Sendable (Sound) async -> Void - public var secondaryAudioShouldBeSilencedHint: @Sendable () async -> Bool + public var secondaryAudioShouldBeSilencedHint: @Sendable () async -> Bool = { false } public var setGlobalVolumeForMusic: @Sendable (Float) async -> Void public var setGlobalVolumeForSoundEffects: @Sendable (Float) async -> Void public var setVolume: @Sendable (Sound, Float) async -> Void diff --git a/Sources/AudioPlayerClient/TestKey.swift b/Sources/AudioPlayerClient/TestKey.swift index 5e9326d7..1f459ce7 100644 --- a/Sources/AudioPlayerClient/TestKey.swift +++ b/Sources/AudioPlayerClient/TestKey.swift @@ -1,5 +1,4 @@ import Dependencies -import XCTestDynamicOverlay extension DependencyValues { public var audioPlayer: AudioPlayerClient { @@ -10,21 +9,7 @@ extension DependencyValues { extension AudioPlayerClient: TestDependencyKey { public static let previewValue = Self.noop - - public static let testValue = Self( - load: unimplemented("\(Self.self).load"), - loop: unimplemented("\(Self.self).loop"), - play: unimplemented("\(Self.self).play"), - secondaryAudioShouldBeSilencedHint: unimplemented( - "\(Self.self).secondaryAudioShouldBeSilencedHint", placeholder: false - ), - setGlobalVolumeForMusic: unimplemented("\(Self.self).setGlobalVolumeForMusic"), - setGlobalVolumeForSoundEffects: unimplemented( - "\(Self.self).setGlobalVolumeForSoundEffects" - ), - setVolume: unimplemented("\(Self.self).setVolume"), - stop: unimplemented("\(Self.self).stop") - ) + public static let testValue = Self() } extension AudioPlayerClient { diff --git a/Sources/Bloom/BloomBackground.swift b/Sources/Bloom/BloomBackground.swift index 7761e228..12062a0c 100644 --- a/Sources/Bloom/BloomBackground.swift +++ b/Sources/Bloom/BloomBackground.swift @@ -110,7 +110,7 @@ public struct BloomBackground: View { public var body: some View { Blooms(blooms: self.blooms) - .onChange(of: self.viewStore.bloomCount) { count in + .onChange(of: self.viewStore.bloomCount) { _, count in withAnimation(.easeOut(duration: 1)) { self.renderBlooms(count: count) } diff --git a/Sources/Build/Build.swift b/Sources/Build/Build.swift index f2bf82dd..43eb6ed1 100644 --- a/Sources/Build/Build.swift +++ b/Sources/Build/Build.swift @@ -1,21 +1,14 @@ import Dependencies +import DependenciesMacros import Foundation import Tagged -import XCTestDynamicOverlay +@DependencyClient public struct Build { - public var gitSha: () -> String - public var number: () -> Number + public var gitSha: () -> String = { "deadbeef" } + public var number: () -> Number = { 0 } public typealias Number = Tagged<((), number: ()), Int> - - public init( - gitSha: @escaping () -> String, - number: @escaping () -> Number - ) { - self.gitSha = gitSha - self.number = number - } } extension DependencyValues { @@ -27,11 +20,7 @@ extension DependencyValues { extension Build: TestDependencyKey { public static let previewValue = Self.noop - - public static let testValue = Self( - gitSha: unimplemented("\(Self.self).gitSha", placeholder: "deadbeef"), - number: unimplemented("\(Self.self).number", placeholder: 0) - ) + public static let testValue = Self() } extension Build: DependencyKey { diff --git a/Sources/ComposableGameCenter/Interface.swift b/Sources/ComposableGameCenter/Interface.swift index 300ed28a..7e2b40e7 100644 --- a/Sources/ComposableGameCenter/Interface.swift +++ b/Sources/ComposableGameCenter/Interface.swift @@ -3,6 +3,7 @@ import ComposableArchitecture import GameKit import Tagged +@DependencyClient public struct GameCenterClient { public var gameCenterViewController: GameCenterViewControllerClient public var localPlayer: LocalPlayerClient @@ -22,15 +23,17 @@ public struct GameCenterClient { } } +@DependencyClient public struct GameCenterViewControllerClient { public var present: @Sendable () async -> Void public var dismiss: @Sendable () async -> Void } +@DependencyClient public struct LocalPlayerClient { public var authenticate: @Sendable () async throws -> Void - public var listener: @Sendable () -> AsyncStream - public var localPlayer: @Sendable () -> LocalPlayer + public var listener: @Sendable () -> AsyncStream = { .finished } + public var localPlayer: @Sendable () -> LocalPlayer = { .mock } public var presentAuthenticationViewController: @Sendable () async -> Void @CasePathable @@ -73,6 +76,7 @@ public struct LocalPlayerClient { } } +@DependencyClient public struct TurnBasedMatchClient { public var endMatchInTurn: @Sendable (EndMatchInTurnRequest) async throws -> Void public var endTurn: @Sendable (EndTurnRequest) async throws -> Void @@ -143,11 +147,8 @@ public struct TurnBasedMatchClient { } } +@DependencyClient public struct TurnBasedMatchmakerViewControllerClient { public var present: @Sendable (_ showExistingMatches: Bool) async throws -> Void public var dismiss: @Sendable () async -> Void - - public func present(showExistingMatches: Bool = true) async throws { - try await self.present(showExistingMatches) - } } diff --git a/Sources/ComposableGameCenter/TestKey.swift b/Sources/ComposableGameCenter/TestKey.swift index c0681308..78cd0d00 100644 --- a/Sources/ComposableGameCenter/TestKey.swift +++ b/Sources/ComposableGameCenter/TestKey.swift @@ -1,5 +1,4 @@ import Dependencies -import XCTestDynamicOverlay extension DependencyValues { public var gameCenter: GameCenterClient { @@ -14,8 +13,6 @@ extension GameCenterClient: TestDependencyKey { public static let testValue = Self( gameCenterViewController: .testValue, localPlayer: .testValue, - reportAchievements: unimplemented("\(Self.self).reportAchievements"), - showNotificationBanner: unimplemented("\(Self.self).showNotificationBanner"), turnBasedMatch: .testValue, turnBasedMatchmakerViewController: .testValue ) @@ -38,10 +35,7 @@ extension GameCenterViewControllerClient { dismiss: {} ) - public static let testValue = Self( - present: unimplemented("\(Self.self).present"), - dismiss: unimplemented("\(Self.self).dismiss") - ) + public static let testValue = Self() } extension LocalPlayerClient { @@ -60,14 +54,7 @@ extension LocalPlayerClient { presentAuthenticationViewController: {} ) - public static let testValue = Self( - authenticate: unimplemented("\(Self.self).authenticate"), - listener: unimplemented("\(Self.self).listener", placeholder: .finished), - localPlayer: unimplemented("\(Self.self).localPlayer", placeholder: .notAuthenticated), - presentAuthenticationViewController: unimplemented( - "\(Self.self).presentAuthenticationViewController" - ) - ) + public static let testValue = Self() } extension TurnBasedMatchClient { @@ -84,18 +71,7 @@ extension TurnBasedMatchClient { sendReminder: { _ in } ) - public static let testValue = Self( - endMatchInTurn: unimplemented("\(Self.self).endMatchInTurn"), - endTurn: unimplemented("\(Self.self).endTurn"), - load: unimplemented("\(Self.self).load"), - loadMatches: unimplemented("\(Self.self).loadMatches"), - participantQuitInTurn: unimplemented("\(Self.self).participantQuitInTurn"), - participantQuitOutOfTurn: unimplemented("\(Self.self).participantQuitOutOfTurn"), - rematch: unimplemented("\(Self.self).rematch"), - remove: unimplemented("\(Self.self).remove"), - saveCurrentTurn: unimplemented("\(Self.self).saveCurrentTurn"), - sendReminder: unimplemented("\(Self.self).sendReminder") - ) + public static let testValue = Self() } extension TurnBasedMatchmakerViewControllerClient { @@ -104,8 +80,5 @@ extension TurnBasedMatchmakerViewControllerClient { dismiss: {} ) - public static let testValue = Self( - present: unimplemented("\(Self.self).present"), - dismiss: unimplemented("\(Self.self).dismiss") - ) + public static let testValue = Self() } diff --git a/Sources/ComposableStoreKit/Client.swift b/Sources/ComposableStoreKit/Client.swift index c93cbb03..bad4705d 100644 --- a/Sources/ComposableStoreKit/Client.swift +++ b/Sources/ComposableStoreKit/Client.swift @@ -1,13 +1,15 @@ import CasePaths +import DependenciesMacros import StoreKit +@DependencyClient public struct StoreKitClient { public var addPayment: @Sendable (SKPayment) async -> Void public var appStoreReceiptURL: @Sendable () -> URL? - public var isAuthorizedForPayments: @Sendable () -> Bool + public var isAuthorizedForPayments: @Sendable () -> Bool = { false } public var fetchProducts: @Sendable (Set) async throws -> ProductsResponse public var finishTransaction: @Sendable (PaymentTransaction) async -> Void - public var observer: @Sendable () -> AsyncStream + public var observer: @Sendable () -> AsyncStream = { .finished } public var requestReview: @Sendable () async -> Void public var restoreCompletedTransactions: @Sendable () async -> Void @@ -124,51 +126,3 @@ public struct StoreKitClient { } } } - -extension SKPaymentTransactionState { - public var canBeVerified: Bool { - switch self { - case .purchasing, .failed, .deferred: - return false - case .purchased, .restored: - return true - @unknown default: - return false - } - } -} - -extension StoreKitClient.PaymentTransaction { - init(rawValue: SKPaymentTransaction) { - self.error = rawValue.error as NSError? - self._original = { rawValue.original.map(Self.init(rawValue:)) } - self.payment = .init(rawValue: rawValue.payment) - self.rawValue = rawValue - self.transactionDate = rawValue.transactionDate - self.transactionIdentifier = rawValue.transactionIdentifier - self.transactionState = rawValue.transactionState - } -} - -extension StoreKitClient.Payment { - init(rawValue: SKPayment) { - self.applicationUsername = rawValue.applicationUsername - self.productIdentifier = rawValue.productIdentifier - self.quantity = rawValue.quantity - self.requestData = rawValue.requestData - self.simulatesAskToBuyInSandbox = rawValue.simulatesAskToBuyInSandbox - } -} - -extension StoreKitClient.Product { - init(rawValue: SKProduct) { - self.downloadContentLengths = rawValue.downloadContentLengths - self.downloadContentVersion = rawValue.downloadContentVersion - self.isDownloadable = rawValue.isDownloadable - self.localizedDescription = rawValue.localizedDescription - self.localizedTitle = rawValue.localizedTitle - self.price = rawValue.price - self.priceLocale = rawValue.priceLocale - self.productIdentifier = rawValue.productIdentifier - } -} diff --git a/Sources/ComposableStoreKit/Support.swift b/Sources/ComposableStoreKit/Support.swift new file mode 100644 index 00000000..b225b1de --- /dev/null +++ b/Sources/ComposableStoreKit/Support.swift @@ -0,0 +1,49 @@ +import StoreKit + +extension SKPaymentTransactionState { + public var canBeVerified: Bool { + switch self { + case .purchasing, .failed, .deferred: + return false + case .purchased, .restored: + return true + @unknown default: + return false + } + } +} + +extension StoreKitClient.PaymentTransaction { + init(rawValue: SKPaymentTransaction) { + self.error = rawValue.error as NSError? + self._original = { rawValue.original.map(Self.init(rawValue:)) } + self.payment = .init(rawValue: rawValue.payment) + self.rawValue = rawValue + self.transactionDate = rawValue.transactionDate + self.transactionIdentifier = rawValue.transactionIdentifier + self.transactionState = rawValue.transactionState + } +} + +extension StoreKitClient.Payment { + init(rawValue: SKPayment) { + self.applicationUsername = rawValue.applicationUsername + self.productIdentifier = rawValue.productIdentifier + self.quantity = rawValue.quantity + self.requestData = rawValue.requestData + self.simulatesAskToBuyInSandbox = rawValue.simulatesAskToBuyInSandbox + } +} + +extension StoreKitClient.Product { + init(rawValue: SKProduct) { + self.downloadContentLengths = rawValue.downloadContentLengths + self.downloadContentVersion = rawValue.downloadContentVersion + self.isDownloadable = rawValue.isDownloadable + self.localizedDescription = rawValue.localizedDescription + self.localizedTitle = rawValue.localizedTitle + self.price = rawValue.price + self.priceLocale = rawValue.priceLocale + self.productIdentifier = rawValue.productIdentifier + } +} diff --git a/Sources/ComposableStoreKit/TestKey.swift b/Sources/ComposableStoreKit/TestKey.swift index 299c1118..13176be8 100644 --- a/Sources/ComposableStoreKit/TestKey.swift +++ b/Sources/ComposableStoreKit/TestKey.swift @@ -1,5 +1,4 @@ import Dependencies -import XCTestDynamicOverlay extension DependencyValues { public var storeKit: StoreKitClient { @@ -10,21 +9,7 @@ extension DependencyValues { extension StoreKitClient: TestDependencyKey { public static let previewValue = Self.noop - - public static let testValue = Self( - addPayment: unimplemented("\(Self.self).addPayment"), - appStoreReceiptURL: unimplemented("\(Self.self).appStoreReceiptURL", placeholder: nil), - isAuthorizedForPayments: unimplemented( - "\(Self.self).isAuthorizedForPayments", placeholder: false - ), - fetchProducts: unimplemented("\(Self.self).fetchProducts"), - finishTransaction: unimplemented("\(Self.self).finishTransaction"), - observer: unimplemented("\(Self.self).observer", placeholder: .finished), - requestReview: unimplemented("\(Self.self).requestReview"), - restoreCompletedTransactions: unimplemented( - "\(Self.self).restoreCompletedTransactions" - ) - ) + public static let testValue = Self() } extension StoreKitClient { @@ -34,7 +19,7 @@ extension StoreKitClient { isAuthorizedForPayments: { false }, fetchProducts: { _ in try await Task.never() }, finishTransaction: { _ in }, - observer: { AsyncStream { _ in } }, + observer: { .never }, requestReview: {}, restoreCompletedTransactions: {} ) diff --git a/Sources/ComposableUserNotifications/Interface.swift b/Sources/ComposableUserNotifications/Interface.swift index 5a96d5c7..45f3f058 100644 --- a/Sources/ComposableUserNotifications/Interface.swift +++ b/Sources/ComposableUserNotifications/Interface.swift @@ -2,10 +2,13 @@ import Combine import ComposableArchitecture import UserNotifications +@DependencyClient public struct UserNotificationClient { public var add: @Sendable (UNNotificationRequest) async throws -> Void - public var delegate: @Sendable () -> AsyncStream - public var getNotificationSettings: @Sendable () async -> Notification.Settings + public var delegate: @Sendable () -> AsyncStream = { .finished } + public var getNotificationSettings: @Sendable () async -> Notification.Settings = { + Notification.Settings(authorizationStatus: .notDetermined) + } public var removeDeliveredNotificationsWithIdentifiers: @Sendable ([String]) async -> Void public var removePendingNotificationRequestsWithIdentifiers: @Sendable ([String]) async -> Void public var requestAuthorization: @Sendable (UNAuthorizationOptions) async throws -> Bool diff --git a/Sources/ComposableUserNotifications/TestKey.swift b/Sources/ComposableUserNotifications/TestKey.swift index e6e408a8..5fe9472e 100644 --- a/Sources/ComposableUserNotifications/TestKey.swift +++ b/Sources/ComposableUserNotifications/TestKey.swift @@ -1,5 +1,4 @@ import Dependencies -import XCTestDynamicOverlay extension DependencyValues { public var userNotifications: UserNotificationClient { @@ -10,20 +9,7 @@ extension DependencyValues { extension UserNotificationClient: TestDependencyKey { public static let previewValue = Self.noop - - public static let testValue = Self( - add: unimplemented("\(Self.self).add"), - delegate: unimplemented("\(Self.self).delegate", placeholder: .finished), - getNotificationSettings: unimplemented( - "\(Self.self).getNotificationSettings", - placeholder: Notification.Settings(authorizationStatus: .notDetermined) - ), - removeDeliveredNotificationsWithIdentifiers: unimplemented( - "\(Self.self).removeDeliveredNotificationsWithIdentifiers"), - removePendingNotificationRequestsWithIdentifiers: unimplemented( - "\(Self.self).removePendingNotificationRequestsWithIdentifiers"), - requestAuthorization: unimplemented("\(Self.self).requestAuthorization") - ) + public static let testValue = Self() } extension UserNotificationClient { diff --git a/Sources/DeviceId/DeviceId.swift b/Sources/DeviceId/DeviceId.swift index 27d31d10..a9a45d30 100644 --- a/Sources/DeviceId/DeviceId.swift +++ b/Sources/DeviceId/DeviceId.swift @@ -1,9 +1,10 @@ import Dependencies +import DependenciesMacros import Foundation -import XCTestDynamicOverlay +@DependencyClient public struct DeviceIdentifier { - public var id: () -> UUID + public var id: () -> UUID = { UUID() } } extension DependencyValues { @@ -15,10 +16,7 @@ extension DependencyValues { extension DeviceIdentifier: TestDependencyKey { public static let previewValue = Self.noop - - public static let testValue = Self( - id: unimplemented("\(Self.self).id", placeholder: UUID()) - ) + public static let testValue = Self() } extension DeviceIdentifier: DependencyKey { diff --git a/Sources/DictionaryClient/Client.swift b/Sources/DictionaryClient/Client.swift index 3be250f6..e1ff28e7 100644 --- a/Sources/DictionaryClient/Client.swift +++ b/Sources/DictionaryClient/Client.swift @@ -1,26 +1,14 @@ +import DependenciesMacros import SharedModels +@DependencyClient public struct DictionaryClient { - public var contains: (String, Language) -> Bool + public var contains: (String, Language) -> Bool = { _, _ in false } public var load: (Language) throws -> Bool public var lookup: ((String, Language) -> Lookup?)? - public var randomCubes: (Language) -> Puzzle + public var randomCubes: (Language) -> Puzzle = { _ in .mock } public var unload: (Language) -> Void - public init( - contains: @escaping (String, Language) -> Bool, - load: @escaping (Language) throws -> Bool, - lookup: ((String, Language) -> Lookup?)?, - randomCubes: @escaping (Language) -> Puzzle, - unload: @escaping (Language) -> Void - ) { - self.contains = contains - self.lookup = lookup - self.load = load - self.randomCubes = randomCubes - self.unload = unload - } - public enum Lookup: Equatable { case prefix case word diff --git a/Sources/DictionaryClient/TestKey.swift b/Sources/DictionaryClient/TestKey.swift index d2bb3ead..e464be56 100644 --- a/Sources/DictionaryClient/TestKey.swift +++ b/Sources/DictionaryClient/TestKey.swift @@ -1,6 +1,5 @@ import Dependencies import SharedModels -import XCTestDynamicOverlay extension DependencyValues { public var dictionary: DictionaryClient { @@ -11,14 +10,7 @@ extension DependencyValues { extension DictionaryClient: TestDependencyKey { public static let previewValue = Self.everyString - - public static let testValue = Self( - contains: unimplemented("\(Self.self).contains", placeholder: false), - load: unimplemented("\(Self.self).load", placeholder: false), - lookup: unimplemented("\(Self.self).lookup"), - randomCubes: unimplemented("\(Self.self).randomCubes", placeholder: .mock), - unload: unimplemented("\(Self.self).unload") - ) + public static let testValue = Self() } extension DictionaryClient { diff --git a/Sources/FeedbackGeneratorClient/Client.swift b/Sources/FeedbackGeneratorClient/Client.swift index 65030953..c6a57ae7 100644 --- a/Sources/FeedbackGeneratorClient/Client.swift +++ b/Sources/FeedbackGeneratorClient/Client.swift @@ -1,3 +1,6 @@ +import DependenciesMacros + +@DependencyClient public struct FeedbackGeneratorClient { public var prepare: @Sendable () async -> Void public var selectionChanged: @Sendable () async -> Void diff --git a/Sources/FeedbackGeneratorClient/TestKey.swift b/Sources/FeedbackGeneratorClient/TestKey.swift index f1757a56..aab12b14 100644 --- a/Sources/FeedbackGeneratorClient/TestKey.swift +++ b/Sources/FeedbackGeneratorClient/TestKey.swift @@ -1,5 +1,4 @@ import Dependencies -import XCTestDynamicOverlay extension DependencyValues { public var feedbackGenerator: FeedbackGeneratorClient { @@ -10,11 +9,7 @@ extension DependencyValues { extension FeedbackGeneratorClient: TestDependencyKey { public static let previewValue = Self.noop - - public static let testValue = Self( - prepare: unimplemented("\(Self.self).prepare"), - selectionChanged: unimplemented("\(Self.self).selectionChanged") - ) + public static let testValue = Self() } extension FeedbackGeneratorClient { diff --git a/Sources/FileClient/Client.swift b/Sources/FileClient/Client.swift index 4e4ebac5..7f38a397 100644 --- a/Sources/FileClient/Client.swift +++ b/Sources/FileClient/Client.swift @@ -1,5 +1,7 @@ +import DependenciesMacros import Foundation +@DependencyClient public struct FileClient { public var delete: @Sendable (String) async throws -> Void public var load: @Sendable (String) async throws -> Data diff --git a/Sources/FileClient/TestKey.swift b/Sources/FileClient/TestKey.swift index 8e2db3a8..f9a9d10b 100644 --- a/Sources/FileClient/TestKey.swift +++ b/Sources/FileClient/TestKey.swift @@ -1,7 +1,6 @@ import Dependencies import Foundation import XCTestDebugSupport -import XCTestDynamicOverlay extension DependencyValues { public var fileClient: FileClient { @@ -12,12 +11,7 @@ extension DependencyValues { extension FileClient: TestDependencyKey { public static let previewValue = Self.noop - - public static let testValue = Self( - delete: unimplemented("\(Self.self).deleteAsync"), - load: unimplemented("\(Self.self).loadAsync"), - save: unimplemented("\(Self.self).saveAsync") - ) + public static let testValue = Self() } extension FileClient { diff --git a/Sources/GameCore/Views/GameFooterView.swift b/Sources/GameCore/Views/GameFooterView.swift index 05ddd345..601e8e90 100644 --- a/Sources/GameCore/Views/GameFooterView.swift +++ b/Sources/GameCore/Views/GameFooterView.swift @@ -116,7 +116,7 @@ public struct WordListView: View { guard self.isLeftToRight else { return } reader.scrollTo(SpacerId(), anchor: self.isLeftToRight ? .trailing : .leading) } - .onChange(of: self.viewStore.words) { _ in + .onChange(of: self.viewStore.words) { guard self.isLeftToRight else { return } withAnimation { reader.scrollTo(SpacerId(), anchor: self.isLeftToRight ? .trailing : .leading) diff --git a/Sources/GameCore/Views/GameHeaderView.swift b/Sources/GameCore/Views/GameHeaderView.swift index dbf62565..026449d5 100644 --- a/Sources/GameCore/Views/GameHeaderView.swift +++ b/Sources/GameCore/Views/GameHeaderView.swift @@ -111,7 +111,7 @@ struct ScoreView: View { .foregroundColor(.white) .colorMultiply(self.isTimeAccented ? .red : .adaptiveBlack) .scaleEffect(self.isTimeAccented ? 1.5 : 1) - .onChange(of: self.viewStore.secondsRemaining) { secondsRemaining in + .onChange(of: self.viewStore.secondsRemaining) { _, secondsRemaining in guard secondsRemaining == 10 || (secondsRemaining <= 5 && secondsRemaining > 0) else { return } diff --git a/Sources/GameCore/Views/PlayersAndScoresView.swift b/Sources/GameCore/Views/PlayersAndScoresView.swift index 51970c90..c12309fb 100644 --- a/Sources/GameCore/Views/PlayersAndScoresView.swift +++ b/Sources/GameCore/Views/PlayersAndScoresView.swift @@ -65,12 +65,12 @@ struct PlayersAndScoresView: View { self.yourImage = image } } - .onChange(of: self.viewStore.opponent) { player in + .onChange(of: self.viewStore.opponent) { _, player in player?.rawValue?.loadPhoto(for: .small) { image, _ in self.opponentImage = image } } - .onChange(of: self.viewStore.you) { player in + .onChange(of: self.viewStore.you) { _, player in player?.rawValue?.loadPhoto(for: .small) { image, _ in self.yourImage = image } diff --git a/Sources/LocalDatabaseClient/Interface.swift b/Sources/LocalDatabaseClient/Interface.swift index 748ee0ac..7e7eb88f 100644 --- a/Sources/LocalDatabaseClient/Interface.swift +++ b/Sources/LocalDatabaseClient/Interface.swift @@ -2,6 +2,7 @@ import ComposableArchitecture import Foundation import SharedModels +@DependencyClient public struct LocalDatabaseClient { public var fetchGamesForWord: @Sendable (String) async throws -> [LocalDatabaseClient.Game] public var fetchStats: @Sendable () async throws -> Stats diff --git a/Sources/LocalDatabaseClient/TestKey.swift b/Sources/LocalDatabaseClient/TestKey.swift index 634487ea..8292a27e 100644 --- a/Sources/LocalDatabaseClient/TestKey.swift +++ b/Sources/LocalDatabaseClient/TestKey.swift @@ -1,6 +1,5 @@ import ComposableArchitecture import SharedModels -import XCTestDynamicOverlay extension DependencyValues { public var database: LocalDatabaseClient { @@ -11,15 +10,7 @@ extension DependencyValues { extension LocalDatabaseClient: TestDependencyKey { public static let previewValue = Self.mock - - public static let testValue = Self( - fetchGamesForWord: unimplemented("\(Self.self).fetchGamesForWord"), - fetchStats: unimplemented("\(Self.self).fetchStats"), - fetchVocab: unimplemented("\(Self.self).fetchVocab"), - migrate: unimplemented("\(Self.self).migrate"), - playedGamesCount: unimplemented("\(Self.self).playedGamesCount"), - saveGame: unimplemented("\(Self.self).saveGame") - ) + public static let testValue = Self() } extension LocalDatabaseClient { diff --git a/Sources/LowPowerModeClient/Client.swift b/Sources/LowPowerModeClient/Client.swift index ed6773ce..9a1af71e 100644 --- a/Sources/LowPowerModeClient/Client.swift +++ b/Sources/LowPowerModeClient/Client.swift @@ -1,3 +1,6 @@ +import DependenciesMacros + +@DependencyClient public struct LowPowerModeClient { - public var start: @Sendable () async -> AsyncStream + public var start: @Sendable () async -> AsyncStream = { .finished } } diff --git a/Sources/LowPowerModeClient/TestKey.swift b/Sources/LowPowerModeClient/TestKey.swift index c5cf4227..67f97658 100644 --- a/Sources/LowPowerModeClient/TestKey.swift +++ b/Sources/LowPowerModeClient/TestKey.swift @@ -1,6 +1,5 @@ import ComposableArchitecture import Foundation -import XCTestDynamicOverlay extension DependencyValues { public var lowPowerMode: LowPowerModeClient { @@ -11,10 +10,7 @@ extension DependencyValues { extension LowPowerModeClient: TestDependencyKey { public static let previewValue = Self.true - - public static let testValue = Self( - start: unimplemented("\(Self.self).start") - ) + public static let testValue = Self() } extension LowPowerModeClient { diff --git a/Sources/RemoteNotificationsClient/Interface.swift b/Sources/RemoteNotificationsClient/Interface.swift index 7d985f63..a17e9372 100644 --- a/Sources/RemoteNotificationsClient/Interface.swift +++ b/Sources/RemoteNotificationsClient/Interface.swift @@ -1,5 +1,8 @@ +import DependenciesMacros + +@DependencyClient public struct RemoteNotificationsClient { - public var isRegistered: @Sendable () async -> Bool + public var isRegistered: @Sendable () async -> Bool = { false } public var register: @Sendable () async -> Void public var unregister: @Sendable () async -> Void } diff --git a/Sources/RemoteNotificationsClient/TestKey.swift b/Sources/RemoteNotificationsClient/TestKey.swift index 6fe33857..b67aa4a6 100644 --- a/Sources/RemoteNotificationsClient/TestKey.swift +++ b/Sources/RemoteNotificationsClient/TestKey.swift @@ -1,5 +1,4 @@ import Dependencies -import XCTestDynamicOverlay extension DependencyValues { public var remoteNotifications: RemoteNotificationsClient { @@ -10,12 +9,7 @@ extension DependencyValues { extension RemoteNotificationsClient: TestDependencyKey { public static let previewValue = Self.noop - - public static let testValue = Self( - isRegistered: unimplemented("\(Self.self).isRegistered", placeholder: false), - register: unimplemented("\(Self.self).register"), - unregister: unimplemented("\(Self.self).unregister") - ) + public static let testValue = Self() } extension RemoteNotificationsClient { diff --git a/Sources/ServerConfigClient/Client.swift b/Sources/ServerConfigClient/Client.swift index 8d6cff9c..2b85d77d 100644 --- a/Sources/ServerConfigClient/Client.swift +++ b/Sources/ServerConfigClient/Client.swift @@ -1,6 +1,8 @@ +import DependenciesMacros @_exported import ServerConfig +@DependencyClient public struct ServerConfigClient { - public var config: () -> ServerConfig + public var config: () -> ServerConfig = { ServerConfig() } public var refresh: @Sendable () async throws -> ServerConfig } diff --git a/Sources/ServerConfigClient/TestKey.swift b/Sources/ServerConfigClient/TestKey.swift index 6daa222d..3ba47878 100644 --- a/Sources/ServerConfigClient/TestKey.swift +++ b/Sources/ServerConfigClient/TestKey.swift @@ -1,5 +1,4 @@ import Dependencies -import XCTestDynamicOverlay extension DependencyValues { public var serverConfig: ServerConfigClient { @@ -10,11 +9,7 @@ extension DependencyValues { extension ServerConfigClient: TestDependencyKey { public static let previewValue = Self.noop - - public static let testValue = Self( - config: unimplemented("\(Self.self).config", placeholder: ServerConfig()), - refresh: unimplemented("\(Self.self).refresh") - ) + public static let testValue = Self() } extension ServerConfigClient { diff --git a/Sources/SiteMiddleware/ServerEnvironment.swift b/Sources/SiteMiddleware/ServerEnvironment.swift index c6013db6..11d7e236 100644 --- a/Sources/SiteMiddleware/ServerEnvironment.swift +++ b/Sources/SiteMiddleware/ServerEnvironment.swift @@ -1,3 +1,4 @@ +import DependenciesMacros import DatabaseClient import DictionaryClient import EnvVars @@ -11,41 +12,18 @@ import SnsClient import URLRouting import VerifyReceiptMiddleware +@DependencyClient public struct ServerEnvironment { - public var changelog: () -> Changelog + public var changelog: () -> Changelog = { .current } public var database: DatabaseClient - public var date: () -> Date + public var date: () -> Date = { Date() } public var dictionary: DictionaryClient - public var itunes: ItunesClient public var envVars: EnvVars + public var itunes: ItunesClient public var mailgun: MailgunClient - public var randomCubes: () -> ArchivablePuzzle + public var randomCubes: () -> ArchivablePuzzle = { .mock } public var router: ServerRouter public var snsClient: SnsClient - - public init( - changelog: @escaping () -> Changelog, - database: DatabaseClient, - date: @escaping () -> Date, - dictionary: DictionaryClient, - envVars: EnvVars, - itunes: ItunesClient, - mailgun: MailgunClient, - randomCubes: @escaping () -> ArchivablePuzzle, - router: ServerRouter, - snsClient: SnsClient - ) { - self.changelog = changelog - self.database = database - self.date = date - self.dictionary = dictionary - self.envVars = envVars - self.itunes = itunes - self.mailgun = mailgun - self.randomCubes = randomCubes - self.router = router - self.snsClient = snsClient - } } #if DEBUG @@ -53,14 +31,11 @@ public struct ServerEnvironment { extension ServerEnvironment { public static let testValue = Self( - changelog: unimplemented("\(Self.self).changelog", placeholder: .current), database: .testValue, - date: unimplemented("\(Self.self).date", placeholder: Date()), dictionary: .testValue, envVars: EnvVars(appEnv: .testing), itunes: .testValue, mailgun: .testValue, - randomCubes: unimplemented("\(Self.self).randomCubes", placeholder: .mock), router: .testValue, snsClient: .testValue ) diff --git a/Sources/UIApplicationClient/Client.swift b/Sources/UIApplicationClient/Client.swift index 88527280..5e4925ea 100644 --- a/Sources/UIApplicationClient/Client.swift +++ b/Sources/UIApplicationClient/Client.swift @@ -1,14 +1,18 @@ +import DependenciesMacros import UIKit +@DependencyClient public struct UIApplicationClient { // TODO: Should these endpoints be merged and `@MainActor`? Should `Reducer` be `@MainActor`? public var alternateIconName: () -> String? public var alternateIconNameAsync: @Sendable () async -> String? - public var open: @Sendable (URL, [UIApplication.OpenExternalURLOptionsKey: Any]) async -> Bool - public var openSettingsURLString: @Sendable () async -> String + public var open: @Sendable (URL, [UIApplication.OpenExternalURLOptionsKey: Any]) async -> Bool = { + _, _ in false + } + public var openSettingsURLString: @Sendable () async -> String = { "" } public var setAlternateIconName: @Sendable (String?) async throws -> Void // TODO: Should these endpoints be merged and `@MainActor`? Should `Reducer` be `@MainActor`? public var setUserInterfaceStyle: @Sendable (UIUserInterfaceStyle) async -> Void - @available(*, deprecated) public var supportsAlternateIcons: () -> Bool - public var supportsAlternateIconsAsync: @Sendable () async -> Bool + @available(*, deprecated) public var supportsAlternateIcons: () -> Bool = { false } + public var supportsAlternateIconsAsync: @Sendable () async -> Bool = { false } } diff --git a/Sources/UIApplicationClient/TestKey.swift b/Sources/UIApplicationClient/TestKey.swift index dd477b5c..dbbb617e 100644 --- a/Sources/UIApplicationClient/TestKey.swift +++ b/Sources/UIApplicationClient/TestKey.swift @@ -1,5 +1,4 @@ import Dependencies -import XCTestDynamicOverlay extension DependencyValues { public var applicationClient: UIApplicationClient { @@ -10,21 +9,7 @@ extension DependencyValues { extension UIApplicationClient: TestDependencyKey { public static let previewValue = Self.noop - - public static let testValue = Self( - alternateIconName: unimplemented("\(Self.self).alternateIconName"), - alternateIconNameAsync: unimplemented("\(Self.self).alternateIconNameAsync"), - open: unimplemented("\(Self.self).open", placeholder: false), - openSettingsURLString: unimplemented("\(Self.self).openSettingsURLString"), - setAlternateIconName: unimplemented("\(Self.self).setAlternateIconName"), - setUserInterfaceStyle: unimplemented("\(Self.self).setUserInterfaceStyle"), - supportsAlternateIcons: unimplemented( - "\(Self.self).supportsAlternateIcons", placeholder: false - ), - supportsAlternateIconsAsync: unimplemented( - "\(Self.self).setAlternateIconNameAsync", placeholder: false - ) - ) + public static let testValue = Self() } extension UIApplicationClient { diff --git a/Sources/UserDefaultsClient/Interface.swift b/Sources/UserDefaultsClient/Interface.swift index ca7bcd30..5ad37db0 100644 --- a/Sources/UserDefaultsClient/Interface.swift +++ b/Sources/UserDefaultsClient/Interface.swift @@ -1,4 +1,5 @@ import Dependencies +import DependenciesMacros import Foundation extension DependencyValues { @@ -8,11 +9,12 @@ extension DependencyValues { } } +@DependencyClient public struct UserDefaultsClient { - public var boolForKey: @Sendable (String) -> Bool + public var boolForKey: @Sendable (String) -> Bool = { _ in false } public var dataForKey: @Sendable (String) -> Data? - public var doubleForKey: @Sendable (String) -> Double - public var integerForKey: @Sendable (String) -> Int + public var doubleForKey: @Sendable (String) -> Double = { _ in 0 } + public var integerForKey: @Sendable (String) -> Int = { _ in 0 } public var remove: @Sendable (String) async -> Void public var setBool: @Sendable (Bool, String) async -> Void public var setData: @Sendable (Data?, String) async -> Void diff --git a/Sources/UserDefaultsClient/TestKey.swift b/Sources/UserDefaultsClient/TestKey.swift index b20a3b02..64b6a005 100644 --- a/Sources/UserDefaultsClient/TestKey.swift +++ b/Sources/UserDefaultsClient/TestKey.swift @@ -1,21 +1,9 @@ import Dependencies import Foundation -import XCTestDynamicOverlay extension UserDefaultsClient: TestDependencyKey { public static let previewValue = Self.noop - - public static let testValue = Self( - boolForKey: unimplemented("\(Self.self).boolForKey", placeholder: false), - dataForKey: unimplemented("\(Self.self).dataForKey", placeholder: nil), - doubleForKey: unimplemented("\(Self.self).doubleForKey", placeholder: 0), - integerForKey: unimplemented("\(Self.self).integerForKey", placeholder: 0), - remove: unimplemented("\(Self.self).remove"), - setBool: unimplemented("\(Self.self).setBool"), - setData: unimplemented("\(Self.self).setData"), - setDouble: unimplemented("\(Self.self).setDouble"), - setInteger: unimplemented("\(Self.self).setInteger") - ) + public static let testValue = Self() } extension UserDefaultsClient { diff --git a/Tests/AppFeatureTests/TurnBasedTests.swift b/Tests/AppFeatureTests/TurnBasedTests.swift index 08961afb..19bbb842 100644 --- a/Tests/AppFeatureTests/TurnBasedTests.swift +++ b/Tests/AppFeatureTests/TurnBasedTests.swift @@ -113,6 +113,7 @@ class TurnBasedTests: XCTestCase { await store.receive(\.home.serverConfigResponse) { $0.home.hasChangelog = true } + await store.receive(\.home.activeMatchesResponse.success) await store.receive(\.home.dailyChallengeResponse.success) { $0.home.dailyChallenges = dailyChallenges } @@ -120,9 +121,6 @@ class TurnBasedTests: XCTestCase { $0.home.weekInReview = weekInReview } - await self.mainRunLoop.advance() - await store.receive(\.home.activeMatchesResponse.success) - await store.send(.home(.destination(.presented(.multiplayer(.startButtonTapped))))) listener.continuation @@ -340,6 +338,7 @@ class TurnBasedTests: XCTestCase { await store.receive(\.home.serverConfigResponse) { $0.home.hasChangelog = true } + await store.receive(\.home.activeMatchesResponse.success) await store.receive(\.home.dailyChallengeResponse.success) { $0.home.dailyChallenges = dailyChallenges } @@ -347,8 +346,6 @@ class TurnBasedTests: XCTestCase { $0.home.weekInReview = weekInReview } - await store.receive(\.home.activeMatchesResponse.success) - listener.continuation .yield(.turnBased(.receivedTurnEventForMatch(.inProgress, didBecomeActive: true))) @@ -442,13 +439,13 @@ class TurnBasedTests: XCTestCase { await store.receive(\.home.serverConfigResponse) { $0.home.hasChangelog = true } + await store.receive(\.home.activeMatchesResponse.success) await store.receive(\.home.dailyChallengeResponse.success) { $0.home.dailyChallenges = dailyChallenges } await store.receive(\.home.weekInReviewResponse.success) { $0.home.weekInReview = weekInReview } - await store.receive(\.home.activeMatchesResponse.success) listener.continuation .yield(.turnBased(.receivedTurnEventForMatch(.forfeited, didBecomeActive: true)))