diff --git a/.github/workflows/ci_local_package.yml b/.github/workflows/ci_local_package.yml new file mode 100644 index 00000000..eb4bf418 --- /dev/null +++ b/.github/workflows/ci_local_package.yml @@ -0,0 +1,46 @@ +name: local_package_test + +on: + push: + branches: ["main"] + paths: + - "LocalPackage/**" + pull_request: + branches: ["**"] + paths: + - "LocalPackage/**" + workflow_dispatch: + +jobs: + test: + runs-on: ubuntu-latest + env: + SWIFT_VERSION: "6.2" + SWIFT_TOOLCHAIN_DIR: /opt/hostedtoolcache/swift-Ubuntu + steps: + - uses: actions/checkout@v4.2.2 + + - name: Cache Swift toolchain + id: cache-swift + uses: actions/cache@v4 + with: + path: ${{ env.SWIFT_TOOLCHAIN_DIR }} + key: swift-toolchain-${{ runner.os }}-${{ env.SWIFT_VERSION }} + + - name: Setup Swift + if: steps.cache-swift.outputs.cache-hit != 'true' + uses: swift-actions/setup-swift@v2 + with: + swift-version: ${{ env.SWIFT_VERSION }} + + - name: Restore Swift PATH from cache + if: steps.cache-swift.outputs.cache-hit == 'true' + run: | + SWIFT_BIN=$(find ${{ env.SWIFT_TOOLCHAIN_DIR }} -name swift -type f -path "*/usr/bin/swift" | head -1) + echo "$(dirname "$SWIFT_BIN")" >> $GITHUB_PATH + + - name: Show Swift version + run: swift --version + + - name: Run tests + run: swift test --package-path LocalPackage diff --git a/.prefire.yml b/.prefire.yml index 99b43a4a..c65d76b6 100644 --- a/.prefire.yml +++ b/.prefire.yml @@ -15,3 +15,5 @@ test_configuration: - UIKit - SwiftUI - MultipeerConnectivity + - HometeDomain + diff --git a/CI.xctestplan b/CI.xctestplan index d3e9b3f6..410271cc 100644 --- a/CI.xctestplan +++ b/CI.xctestplan @@ -31,13 +31,6 @@ "identifier" : "BC1BB2672E909B8C001D168F", "name" : "hometeSnapshotTests" } - }, - { - "target" : { - "containerPath" : "container:homete.xcodeproj", - "identifier" : "BCC854012DB74BBF00C9A44B", - "name" : "hometeTests" - } } ], "version" : 1 diff --git a/LocalPackage/.swiftpm/xcode/xcshareddata/xcschemes/HometeDomain.xcscheme b/LocalPackage/.swiftpm/xcode/xcshareddata/xcschemes/HometeDomain.xcscheme new file mode 100644 index 00000000..969323f0 --- /dev/null +++ b/LocalPackage/.swiftpm/xcode/xcshareddata/xcschemes/HometeDomain.xcscheme @@ -0,0 +1,79 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/LocalPackage/Package.swift b/LocalPackage/Package.swift index 51ae1520..27ef927d 100644 --- a/LocalPackage/Package.swift +++ b/LocalPackage/Package.swift @@ -5,19 +5,20 @@ import PackageDescription let package = Package( name: "LocalPackage", + platforms: [.iOS(.v17), .macOS(.v15)], products: [ .library( - name: "LocalPackage", - targets: ["LocalPackage"] + name: "HometeDomain", + targets: ["HometeDomain"] ), ], targets: [ .target( - name: "LocalPackage" + name: "HometeDomain" ), .testTarget( - name: "LocalPackageTests", - dependencies: ["LocalPackage"] + name: "HometeDomainTests", + dependencies: ["HometeDomain"] ), ] ) diff --git a/LocalPackage/Sources/HometeDomain/Account/Account.swift b/LocalPackage/Sources/HometeDomain/Account/Account.swift new file mode 100644 index 00000000..d8b5eef0 --- /dev/null +++ b/LocalPackage/Sources/HometeDomain/Account/Account.swift @@ -0,0 +1,30 @@ +// +// Account.swift +// homete +// +// Created by 佐藤汰一 on 2025/08/03. +// + +// TODO: ダミー +public struct Account: Equatable, Codable, Sendable { + + public let id: String + public let userName: String + public let fcmToken: String? + public let cohabitantId: String? + + public init(id: String, userName: String, fcmToken: String?, cohabitantId: String?) { + self.id = id + self.userName = userName + self.fcmToken = fcmToken + self.cohabitantId = cohabitantId + } +} + +public extension Account { + + static func initial(auth: AccountAuthResult, userName: UserName, fcmToken: String?) -> Self { + + return .init(id: auth.id, userName: userName.value, fcmToken: fcmToken, cohabitantId: nil) + } +} diff --git a/homete/Model/Domain/Account/AccountStore.swift b/LocalPackage/Sources/HometeDomain/Account/AccountStore.swift similarity index 75% rename from homete/Model/Domain/Account/AccountStore.swift rename to LocalPackage/Sources/HometeDomain/Account/AccountStore.swift index 46de8488..e981f6dd 100644 --- a/homete/Model/Domain/Account/AccountStore.swift +++ b/LocalPackage/Sources/HometeDomain/Account/AccountStore.swift @@ -5,58 +5,58 @@ // Created by 佐藤汰一 on 2025/08/03. // -import SwiftUI +import Observation @MainActor @Observable -final class AccountStore { - - private(set) var account: Account? - +public final class AccountStore { + + public private(set) var account: Account? + private let accountInfoClient: AccountInfoClient - - init( - appDependencies: AppDependencies, + + public init( + accountInfoClient: AccountInfoClient = .previewValue, account: Account? = nil ) { - - accountInfoClient = appDependencies.accountInfoClient + + self.accountInfoClient = accountInfoClient self.account = account } - + /// アカウント情報をロードし、オンメモリにキャッシュする /// - Returns: ロードしたアカウント情報を返す(アカウントがない場合はnilを返す) @discardableResult - func load(_ auth: AccountAuthResult) async -> Account? { - + public func load(_ auth: AccountAuthResult) async -> Account? { + do { - + account = try await accountInfoClient.fetch(auth.id) } catch { - + print("failed to fetch account info: \(error)") } - + return account } - - func registerAccount(auth: AccountAuthResult, userName: UserName) async throws -> Account { - + + public func registerAccount(auth: AccountAuthResult, userName: UserName) async throws -> Account { + let newAccount = Account(id: auth.id, userName: userName.value, fcmToken: nil, cohabitantId: nil) try await accountInfoClient.insertOrUpdate(newAccount) account = newAccount return newAccount } - - func updateFcmTokenIfNeeded(_ fcmToken: String) async { - + + public func updateFcmTokenIfNeeded(_ fcmToken: String) async { + // 保持しているFCMトークンと異なるFCMトークンに変わった場合は、アカウント情報も新しいトークンに更新する guard let account, account.fcmToken != fcmToken else { return } - + do { - + let updatedAccount = Account( id: account.id, userName: account.userName, @@ -67,18 +67,18 @@ final class AccountStore { self.account = updatedAccount } catch { - + print("failed to update fcmToken: \(error)") } } - - func registerCohabitantId(_ cohabitantId: String) async throws { - + + public func registerCohabitantId(_ cohabitantId: String) async throws { + guard let account else { - + preconditionFailure("Not found account.") } - + let updatedAccount = Account( id: account.id, userName: account.userName, diff --git a/LocalPackage/Sources/HometeDomain/Account/LoginContext.swift b/LocalPackage/Sources/HometeDomain/Account/LoginContext.swift new file mode 100644 index 00000000..0b2149b5 --- /dev/null +++ b/LocalPackage/Sources/HometeDomain/Account/LoginContext.swift @@ -0,0 +1,18 @@ +// +// LoginContext.swift +// homete +// +// Created by 佐藤汰一 on 2025/12/27. +// + +public struct LoginContext: Equatable { + + public let account: Account + + /// パートナー登録済みかどうか + public var hasCohabitant: Bool { account.cohabitantId != nil } + + public init(account: Account) { + self.account = account + } +} diff --git a/homete/Model/Domain/Account/UserName.swift b/LocalPackage/Sources/HometeDomain/Account/UserName.swift similarity index 54% rename from homete/Model/Domain/Account/UserName.swift rename to LocalPackage/Sources/HometeDomain/Account/UserName.swift index efa58f02..0c6ce812 100644 --- a/homete/Model/Domain/Account/UserName.swift +++ b/LocalPackage/Sources/HometeDomain/Account/UserName.swift @@ -5,20 +5,24 @@ // Created by 佐藤汰一 on 2025/12/27. // -struct UserName { - var value = "" - +public struct UserName { + public var value = "" + private static let limitCharacters = 10 - - var remainingCharacters: Int { + + public var remainingCharacters: Int { return Self.limitCharacters - value.count } - - var isOverLimitCharacters: Bool { + + public var isOverLimitCharacters: Bool { return Self.limitCharacters < value.count } - - var canRegistration: Bool { + + public var canRegistration: Bool { return !value.isEmpty && !isOverLimitCharacters } + + public init(value: String = "") { + self.value = value + } } diff --git a/homete/Model/Domain/AnalyticsLog/AnalyticsEvent.swift b/LocalPackage/Sources/HometeDomain/AnalyticsLog/AnalyticsEvent.swift similarity index 65% rename from homete/Model/Domain/AnalyticsLog/AnalyticsEvent.swift rename to LocalPackage/Sources/HometeDomain/AnalyticsLog/AnalyticsEvent.swift index ec4d480b..03d6b11a 100644 --- a/homete/Model/Domain/AnalyticsLog/AnalyticsEvent.swift +++ b/LocalPackage/Sources/HometeDomain/AnalyticsLog/AnalyticsEvent.swift @@ -5,32 +5,37 @@ // Created by 佐藤汰一 on 2025/08/09. // -struct AnalyticsEvent: Equatable { - - let name: String - let parameters: [String: String] +public struct AnalyticsEvent: Equatable { + + public let name: String + public let parameters: [String: String] + + public init(name: String, parameters: [String: String]) { + self.name = name + self.parameters = parameters + } } -extension AnalyticsEvent { - +public extension AnalyticsEvent { + static func login(isSuccess: Bool) -> Self { - + return .init( name: "login", parameters: ["isSuccess": "\(isSuccess)"] ) } - + static func logout() -> Self { - + return .init( name: "logout", parameters: [:] ) } - + static func deleteAccount() -> Self { - + return .init( name: "delete_account", parameters: [:] diff --git a/LocalPackage/Sources/HometeDomain/Authentification/AccountAuthInfo.swift b/LocalPackage/Sources/HometeDomain/Authentification/AccountAuthInfo.swift new file mode 100644 index 00000000..a8a47220 --- /dev/null +++ b/LocalPackage/Sources/HometeDomain/Authentification/AccountAuthInfo.swift @@ -0,0 +1,18 @@ +// +// AccountAuthInfo.swift +// homete +// +// Created by 佐藤汰一 on 2025/12/31. +// + +public struct AccountAuthInfo: Equatable, Sendable { + public let result: AccountAuthResult? + public let alreadyLoadedAtInitiate: Bool + + public static let initial = AccountAuthInfo(result: nil, alreadyLoadedAtInitiate: false) + + public init(result: AccountAuthResult?, alreadyLoadedAtInitiate: Bool) { + self.result = result + self.alreadyLoadedAtInitiate = alreadyLoadedAtInitiate + } +} diff --git a/LocalPackage/Sources/HometeDomain/Authentification/AccountAuthResult.swift b/LocalPackage/Sources/HometeDomain/Authentification/AccountAuthResult.swift new file mode 100644 index 00000000..d6504897 --- /dev/null +++ b/LocalPackage/Sources/HometeDomain/Authentification/AccountAuthResult.swift @@ -0,0 +1,15 @@ +// +// AccountAuthResult.swift +// homete +// +// Created by 佐藤汰一 on 2025/08/09. +// + +public struct AccountAuthResult: Equatable, Sendable { + + public let id: String + + public init(id: String) { + self.id = id + } +} diff --git a/homete/Model/Domain/Authentification/AccountAuthStore.swift b/LocalPackage/Sources/HometeDomain/Authentification/AccountAuthStore.swift similarity index 72% rename from homete/Model/Domain/Authentification/AccountAuthStore.swift rename to LocalPackage/Sources/HometeDomain/Authentification/AccountAuthStore.swift index 8b7b4659..86e290d2 100644 --- a/homete/Model/Domain/Authentification/AccountAuthStore.swift +++ b/LocalPackage/Sources/HometeDomain/Authentification/AccountAuthStore.swift @@ -5,96 +5,99 @@ // Created by 佐藤汰一 on 2025/08/09. // -import SwiftUI +import Observation @MainActor @Observable -final class AccountAuthStore { - - private(set) var currentAuth: AccountAuthInfo - +public final class AccountAuthStore { + + public private(set) var currentAuth: AccountAuthInfo + private let accountAuthClient: AccountAuthClient private let analyticsClient: AnalyticsClient private let signInWithAppleClient: SignInWithAppleClient private let nonceGenerationClient: NonceGenerationClient private let listener: AccountListenerStream - - init( - appDependencies: AppDependencies, + + public init( + accountAuthClient: AccountAuthClient = .previewValue, + analyticsClient: AnalyticsClient = .previewValue, + signInWithAppleClient: SignInWithAppleClient = .previewValue, + nonceGenerationClient: NonceGenerationClient = .previewValue, currentAuth: AccountAuthInfo = .initial ) { - - accountAuthClient = appDependencies.accountAuthClient - analyticsClient = appDependencies.analyticsClient - signInWithAppleClient = appDependencies.signInWithAppleClient - nonceGenerationClient = appDependencies.nonceGeneratorClient + + self.accountAuthClient = accountAuthClient + self.analyticsClient = analyticsClient + self.signInWithAppleClient = signInWithAppleClient + self.nonceGenerationClient = nonceGenerationClient self.currentAuth = currentAuth - + listener = accountAuthClient.makeListener() - + Task { - + await listen() } } - - func login(_ signInResult: SignInWithAppleResult) async throws { - + + public func login(_ signInResult: SignInWithAppleResult) async throws { + do { - + let authInfo = try await accountAuthClient.signIn(signInResult.tokenId, signInResult.nonce) analyticsClient.setId(authInfo.id) analyticsClient.log(.login(isSuccess: true)) } catch { - + analyticsClient.log(.login(isSuccess: false)) throw error } } - - func logOut() { - + + public func logOut() { + currentAuth = .init(result: nil, alreadyLoadedAtInitiate: true) analyticsClient.log(.logout()) - + do { - + try accountAuthClient.signOut() } catch { - + print("occurred error: \(error)") } } - - func deleteAccount() async throws { - + + public func deleteAccount() async throws { + // 1. 再認証 let nonce = nonceGenerationClient() let signInWithAppleResult = try await signInWithAppleClient.reauthentication(nonce) try await accountAuthClient.reauthenticateWithApple(signInWithAppleResult) - + // 2. アカウント削除 try await accountAuthClient.deleteAccount() - + // 3. トークンRevoke try await accountAuthClient.revokeAppleToken(signInWithAppleResult.authorizationCode) - + // 4. ログ記録 analyticsClient.log(.deleteAccount()) - + // 5. 状態更新 currentAuth = .init(result: nil, alreadyLoadedAtInitiate: true) } } private extension AccountAuthStore { - + func listen() async { - + for await value in listener.values { - + currentAuth = .init(result: value, alreadyLoadedAtInitiate: true) print("currentAuth snapshot: \(String(describing: currentAuth))") } diff --git a/homete/Model/Domain/Authentification/AccountListenerStream.swift b/LocalPackage/Sources/HometeDomain/Authentification/AccountListenerStream.swift similarity index 72% rename from homete/Model/Domain/Authentification/AccountListenerStream.swift rename to LocalPackage/Sources/HometeDomain/Authentification/AccountListenerStream.swift index 743b3f71..3db9c180 100644 --- a/homete/Model/Domain/Authentification/AccountListenerStream.swift +++ b/LocalPackage/Sources/HometeDomain/Authentification/AccountListenerStream.swift @@ -7,25 +7,25 @@ import Foundation -struct AccountListenerStream { - - let values: AsyncStream - let listenerToken: any NSObjectProtocol +public struct AccountListenerStream { + + public let values: AsyncStream + public let listenerToken: any NSObjectProtocol private let continuation: AsyncStream.Continuation - - init( + + public init( values: AsyncStream, listenerToken: any NSObjectProtocol, continuation: AsyncStream.Continuation ) { - + self.values = values self.listenerToken = listenerToken self.continuation = continuation } - - func stopListening() { - + + public func stopListening() { + continuation.finish() } } diff --git a/homete/Model/Domain/Authentification/LaunchState.swift b/LocalPackage/Sources/HometeDomain/Authentification/LaunchState.swift similarity index 84% rename from homete/Model/Domain/Authentification/LaunchState.swift rename to LocalPackage/Sources/HometeDomain/Authentification/LaunchState.swift index 73625e46..c88f346f 100644 --- a/homete/Model/Domain/Authentification/LaunchState.swift +++ b/LocalPackage/Sources/HometeDomain/Authentification/LaunchState.swift @@ -5,8 +5,8 @@ // Created by 佐藤汰一 on 2025/09/02. // -enum LaunchState: Equatable { - +public enum LaunchState: Equatable { + /// 起動中 case launching /// 仮ログイン(アカウント未作成) @@ -15,9 +15,9 @@ enum LaunchState: Equatable { case loggedIn(context: LoginContext) /// 未ログイン case notLoggedIn - - var isLoggedIn: Bool { - + + public var isLoggedIn: Bool { + if case .loggedIn = self { return true } return false } diff --git a/LocalPackage/Sources/HometeDomain/Authentification/SignInWithAppleNonce.swift b/LocalPackage/Sources/HometeDomain/Authentification/SignInWithAppleNonce.swift new file mode 100644 index 00000000..da647668 --- /dev/null +++ b/LocalPackage/Sources/HometeDomain/Authentification/SignInWithAppleNonce.swift @@ -0,0 +1,17 @@ +// +// SignInWithAppleNonce.swift +// homete +// +// Created by 佐藤汰一 on 2025/08/04. +// + +public struct SignInWithAppleNonce: Equatable, Sendable { + + public let original: String + public let sha256: String + + public init(original: String, sha256: String) { + self.original = original + self.sha256 = sha256 + } +} diff --git a/LocalPackage/Sources/HometeDomain/Authentification/SignInWithAppleResult.swift b/LocalPackage/Sources/HometeDomain/Authentification/SignInWithAppleResult.swift new file mode 100644 index 00000000..94fd6670 --- /dev/null +++ b/LocalPackage/Sources/HometeDomain/Authentification/SignInWithAppleResult.swift @@ -0,0 +1,19 @@ +// +// SignInWithAppleResult.swift +// homete +// +// Created by 佐藤汰一 on 2025/08/09. +// + +public struct SignInWithAppleResult: Equatable, Sendable { + + public let tokenId: String + public let nonce: String + public let authorizationCode: String + + public init(tokenId: String, nonce: String, authorizationCode: String) { + self.tokenId = tokenId + self.nonce = nonce + self.authorizationCode = authorizationCode + } +} diff --git a/LocalPackage/Sources/HometeDomain/Cohabitant/CohabitantData.swift b/LocalPackage/Sources/HometeDomain/Cohabitant/CohabitantData.swift new file mode 100644 index 00000000..c035c421 --- /dev/null +++ b/LocalPackage/Sources/HometeDomain/Cohabitant/CohabitantData.swift @@ -0,0 +1,21 @@ +// +// CohabitantData.swift +// homete +// +// Created by 佐藤汰一 on 2026/01/04. +// + +public struct CohabitantData: Codable, Sendable { + + public static let idField = "id" + + /// 家族グループのID + public let id: String + /// 参加しているメンバーのユーザーID + public let members: [String] + + public init(id: String, members: [String]) { + self.id = id + self.members = members + } +} diff --git a/LocalPackage/Sources/HometeDomain/Cohabitant/CohabitantMember.swift b/LocalPackage/Sources/HometeDomain/Cohabitant/CohabitantMember.swift new file mode 100644 index 00000000..0ad8d2f8 --- /dev/null +++ b/LocalPackage/Sources/HometeDomain/Cohabitant/CohabitantMember.swift @@ -0,0 +1,19 @@ +// +// CohabitantMember.swift +// homete +// +// Created by 佐藤汰一 on 2026/01/04. +// + +public struct CohabitantMember: Equatable, Hashable, Sendable { + + /// メンバーのユーザーID + public let id: String + /// メンバーのユーザー名 + public let userName: String + + public init(id: String, userName: String) { + self.id = id + self.userName = userName + } +} diff --git a/homete/Model/Domain/Cohabitant/CohabitantMemberList.swift b/LocalPackage/Sources/HometeDomain/Cohabitant/CohabitantMemberList.swift similarity index 65% rename from homete/Model/Domain/Cohabitant/CohabitantMemberList.swift rename to LocalPackage/Sources/HometeDomain/Cohabitant/CohabitantMemberList.swift index 6f1591b4..7f2afcc0 100644 --- a/homete/Model/Domain/Cohabitant/CohabitantMemberList.swift +++ b/LocalPackage/Sources/HometeDomain/Cohabitant/CohabitantMemberList.swift @@ -5,29 +5,33 @@ // Created by 佐藤汰一 on 2026/01/04. // -struct CohabitantMemberList { - - private(set) var value: Set - - mutating func insert(_ element: CohabitantMember) { - +public struct CohabitantMemberList: Sendable { + + public private(set) var value: Set + + public init(value: Set) { + self.value = value + } + + public mutating func insert(_ element: CohabitantMember) { + value.insert(element) } - + /// 与えられたユーザーID配列の中から、まだvalueに存在しないユーザーIDのみを返します。 /// - Parameter userIds: 追加するユーザーIDの候補の配列 /// - Returns: 追加が必要なユーザーIDの配列 - func missingMemberIds(from userIds: Set) -> Set { - + public func missingMemberIds(from userIds: Set) -> Set { + let existingIds = value.map(\.id) return userIds.filter { !existingIds.contains($0) } } - + /// 現在の家族グループの中から指定のユーザーIDの名前を取得する /// - Parameter id: ユーザーID /// - Returns: ユーザー名 - func userName(_ id: String) -> String? { - + public func userName(_ id: String) -> String? { + return value.first { $0.id == id }?.userName } } diff --git a/homete/Model/Domain/Cohabitant/CohabitantRegistration/CohabitantRegistrationMessage.swift b/LocalPackage/Sources/HometeDomain/Cohabitant/CohabitantRegistration/CohabitantRegistrationMessage.swift similarity index 74% rename from homete/Model/Domain/Cohabitant/CohabitantRegistration/CohabitantRegistrationMessage.swift rename to LocalPackage/Sources/HometeDomain/Cohabitant/CohabitantRegistration/CohabitantRegistrationMessage.swift index 8f99b3e7..ac9983be 100644 --- a/homete/Model/Domain/Cohabitant/CohabitantRegistration/CohabitantRegistrationMessage.swift +++ b/LocalPackage/Sources/HometeDomain/Cohabitant/CohabitantRegistration/CohabitantRegistrationMessage.swift @@ -7,12 +7,12 @@ import Foundation -struct CohabitantRegistrationMessage: Codable, Equatable { - - let type: CommunicateType - - enum CommunicateType: Codable, Equatable { - +public struct CohabitantRegistrationMessage: Codable, Equatable, Sendable { + + public let type: CommunicateType + + public enum CommunicateType: Codable, Equatable, Sendable { + /// 登録を行うメンバーが確定したかどうかの確認 case fixedMember(isOK: Bool) /// アカウントIDの共有 @@ -22,62 +22,66 @@ struct CohabitantRegistrationMessage: Codable, Equatable { /// 登録完了したかどうかの確認 case complete } - + /// 登録メンバーが確定したかどうか - var isFixedMember: Bool? { - + public var isFixedMember: Bool? { + guard case .fixedMember(let isOK) = type else { return nil } return isOK } - + /// メンバーの役割 - var memberRole: CohabitantRegistrationRole? { - + public var memberRole: CohabitantRegistrationRole? { + guard case .preRegistration(let role) = type else { - + return nil } return role } - + /// 同居人ID - var cohabitantId: String? { - + public var cohabitantId: String? { + guard case .shareCohabitantId(let id) = type else { - + return nil } return id } - + /// 登録処理が完了したかどうか - var isComplete: Bool? { - + public var isComplete: Bool? { + guard case .complete = type else { - + return nil } return true } - - func encodedData() -> Data { - + + public func encodedData() -> Data { + guard let encodedData = try? JSONEncoder().encode(self) else { - + preconditionFailure("Invalid message structure(\(self)).") } return encodedData } + + public init(type: CommunicateType) { + self.type = type + } } -extension CohabitantRegistrationMessage { - +public extension CohabitantRegistrationMessage { + init(_ data: Data) { - + guard let message = try? JSONDecoder().decode(CohabitantRegistrationMessage.self, from: data) else { - + preconditionFailure("Invalid data(\(data)).") } self = message diff --git a/homete/Model/Domain/Cohabitant/CohabitantRegistration/CohabitantRegistrationRole.swift b/LocalPackage/Sources/HometeDomain/Cohabitant/CohabitantRegistration/CohabitantRegistrationRole.swift similarity index 73% rename from homete/Model/Domain/Cohabitant/CohabitantRegistration/CohabitantRegistrationRole.swift rename to LocalPackage/Sources/HometeDomain/Cohabitant/CohabitantRegistration/CohabitantRegistrationRole.swift index fd95989f..497580d9 100644 --- a/homete/Model/Domain/Cohabitant/CohabitantRegistration/CohabitantRegistrationRole.swift +++ b/LocalPackage/Sources/HometeDomain/Cohabitant/CohabitantRegistration/CohabitantRegistrationRole.swift @@ -5,21 +5,21 @@ // Created by 佐藤汰一 on 2025/08/30. // -enum CohabitantRegistrationRole: Codable, Equatable { - +public enum CohabitantRegistrationRole: Codable, Equatable, Sendable { + /// フォロワーはアカウントIDを渡す case follower(accountId: String) case lead - - var isLeader: Bool { - + + public var isLeader: Bool { + return self == .lead } - - var accountId: String { - + + public var accountId: String { + guard case let .follower(accountId) = self else { - + preconditionFailure("Please pre checking role is follower.") } return accountId diff --git a/homete/Model/Domain/Cohabitant/CohabitantRegistration/CohabitantRegistrationState.swift b/LocalPackage/Sources/HometeDomain/Cohabitant/CohabitantRegistration/CohabitantRegistrationState.swift similarity index 86% rename from homete/Model/Domain/Cohabitant/CohabitantRegistration/CohabitantRegistrationState.swift rename to LocalPackage/Sources/HometeDomain/Cohabitant/CohabitantRegistration/CohabitantRegistrationState.swift index c14cf40c..16d813d9 100644 --- a/homete/Model/Domain/Cohabitant/CohabitantRegistration/CohabitantRegistrationState.swift +++ b/LocalPackage/Sources/HometeDomain/Cohabitant/CohabitantRegistration/CohabitantRegistrationState.swift @@ -5,8 +5,8 @@ // Created by 佐藤汰一 on 2025/08/26. // -enum CohabitantRegistrationState: Equatable { - +public enum CohabitantRegistrationState: Equatable { + /// 同居人となるメンバーを探している状態 case scanning /// 同居人を登録する処理を行っている状態 diff --git a/LocalPackage/Sources/HometeDomain/Cohabitant/CohabitantRegistration/ConfirmedRegistrationPeers.swift b/LocalPackage/Sources/HometeDomain/Cohabitant/CohabitantRegistration/ConfirmedRegistrationPeers.swift new file mode 100644 index 00000000..03d5c768 --- /dev/null +++ b/LocalPackage/Sources/HometeDomain/Cohabitant/CohabitantRegistration/ConfirmedRegistrationPeers.swift @@ -0,0 +1,33 @@ +// +// ConfirmedRegistrationPeers.swift +// homete +// +// Created by 佐藤汰一 on 2025/08/30. +// + +#if canImport(MultipeerConnectivity) +import MultipeerConnectivity + +public struct ConfirmedRegistrationPeers: Equatable { + + private var peers: Set + + public init(peers: Set) { + + self.peers = peers + } + + public mutating func addPeer(_ peer: MCPeerID) { + + peers.insert(peer) + } + + public func isLeadPeer(connectedPeers: Set, myPeerID: MCPeerID) -> Bool? { + + guard peers == connectedPeers else { return nil } + let firstPeerID = ([myPeerID] + connectedPeers) + .sorted { $0.displayName < $1.displayName }.first + return firstPeerID == myPeerID + } +} +#endif diff --git a/homete/Model/Domain/Cohabitant/CohabitantStore.swift b/LocalPackage/Sources/HometeDomain/Cohabitant/CohabitantStore.swift similarity index 72% rename from homete/Model/Domain/Cohabitant/CohabitantStore.swift rename to LocalPackage/Sources/HometeDomain/Cohabitant/CohabitantStore.swift index 08d56c7a..250234f3 100644 --- a/homete/Model/Domain/Cohabitant/CohabitantStore.swift +++ b/LocalPackage/Sources/HometeDomain/Cohabitant/CohabitantStore.swift @@ -5,70 +5,74 @@ // Created by 佐藤汰一 on 2026/01/04. // -import SwiftUI +import Observation @MainActor @Observable -final class CohabitantStore { - - private(set) var members: CohabitantMemberList +public final class CohabitantStore { + + public private(set) var members: CohabitantMemberList private var listenerTask: Task? - + private let cohabitantListenerKey = "cohabitantListenerKey" - + // MARK: Dependencies - + private let cohabitantClient: CohabitantClient private let accountInfoClient: AccountInfoClient - - init( + + public init( members: CohabitantMemberList = .init(value: []), - appDependencies: AppDependencies = .previewValue + cohabitantClient: CohabitantClient = .previewValue, + accountInfoClient: AccountInfoClient = .previewValue ) { + self.members = members - cohabitantClient = appDependencies.cohabitantClient - accountInfoClient = appDependencies.accountInfoClient + self.cohabitantClient = cohabitantClient + self.accountInfoClient = accountInfoClient } - - func addSnapshotListenerIfNeeded(_ cohabitantId: String) async { - + + public func addSnapshotListenerIfNeeded(_ cohabitantId: String) async { + // すでに監視中の場合は何もしない if listenerTask != nil { return } - + let stream = await cohabitantClient.addSnapshotListener( cohabitantListenerKey, cohabitantId ) - + listenerTask = Task { - + for await cohabitantDataList in stream { - + guard let cohabitantData = cohabitantDataList.first else { continue } - + for member in self.members.missingMemberIds(from: .init(cohabitantData.members)) { - + do { - + guard let account = try await accountInfoClient.fetch(member) else { - + print("Not found account(cohabitantId: \(cohabitantId), userId: \(member))") continue } members.insert(.init(id: member, userName: account.userName)) } catch { - + print("error occurred: \(error)") } } } - + print("finish listening cohabitant snapshot.") } } - - func removeSnapshotListener() async { - + + public func removeSnapshotListener() async { + + listenerTask?.cancel() + await listenerTask?.value listenerTask = nil await cohabitantClient.removeSnapshotListener(cohabitantListenerKey) } diff --git a/homete/Model/Domain/Cohabitant/Housework/DailyHouseworkList.swift b/LocalPackage/Sources/HometeDomain/Cohabitant/Housework/DailyHouseworkList.swift similarity index 55% rename from homete/Model/Domain/Cohabitant/Housework/DailyHouseworkList.swift rename to LocalPackage/Sources/HometeDomain/Cohabitant/Housework/DailyHouseworkList.swift index c4a5d194..ef459b50 100644 --- a/homete/Model/Domain/Cohabitant/Housework/DailyHouseworkList.swift +++ b/LocalPackage/Sources/HometeDomain/Cohabitant/Housework/DailyHouseworkList.swift @@ -7,29 +7,34 @@ import Foundation -struct DailyHouseworkList: Equatable, Sendable { - - let items: [HouseworkItem] - let metaData: DailyHouseworkMetaData - - static func makeInitialValue( +public struct DailyHouseworkList: Equatable, Sendable { + + public let items: [HouseworkItem] + public let metaData: DailyHouseworkMetaData + + public static func makeInitialValue( selectedDate: Date, items: [HouseworkItem], calendar: Calendar ) -> Self { - + return .init( items: items, metaData: .init(selectedDate: selectedDate, calendar: calendar) ) } - + /// この日付の家事情報がすでに登録済みであること - var isRegistered: Bool { !items.isEmpty } - + public var isRegistered: Bool { !items.isEmpty } + /// すでに同じ家事が登録されているかどうか - func isAlreadyRegistered(_ item: HouseworkItem) -> Bool { - + public func isAlreadyRegistered(_ item: HouseworkItem) -> Bool { + return items.contains { $0.title == item.title } } + + public init(items: [HouseworkItem], metaData: DailyHouseworkMetaData) { + self.items = items + self.metaData = metaData + } } diff --git a/homete/Model/Domain/Cohabitant/Housework/DailyHouseworkMetaData.swift b/LocalPackage/Sources/HometeDomain/Cohabitant/Housework/DailyHouseworkMetaData.swift similarity index 55% rename from homete/Model/Domain/Cohabitant/Housework/DailyHouseworkMetaData.swift rename to LocalPackage/Sources/HometeDomain/Cohabitant/Housework/DailyHouseworkMetaData.swift index 54d59c20..89936bb5 100644 --- a/homete/Model/Domain/Cohabitant/Housework/DailyHouseworkMetaData.swift +++ b/LocalPackage/Sources/HometeDomain/Cohabitant/Housework/DailyHouseworkMetaData.swift @@ -7,16 +7,21 @@ import Foundation -struct DailyHouseworkMetaData: Equatable { - - let indexedDate: HouseworkIndexedDate - let expiredAt: Date +public struct DailyHouseworkMetaData: Equatable, Sendable { + + public let indexedDate: HouseworkIndexedDate + public let expiredAt: Date + + public init(indexedDate: HouseworkIndexedDate, expiredAt: Date) { + self.indexedDate = indexedDate + self.expiredAt = expiredAt + } } -extension DailyHouseworkMetaData { - +public extension DailyHouseworkMetaData { + init(selectedDate: Date, calendar: Calendar) { - + let indexedDate = HouseworkIndexedDate(selectedDate, calendar: calendar) let expiredAt = calendar.date(byAdding: .month, value: 1, to: selectedDate) ?? selectedDate self.init(indexedDate: indexedDate, expiredAt: expiredAt) diff --git a/homete/Model/Domain/Cohabitant/Housework/HouseworkBoardList.swift b/LocalPackage/Sources/HometeDomain/Cohabitant/Housework/HouseworkBoardList.swift similarity index 64% rename from homete/Model/Domain/Cohabitant/Housework/HouseworkBoardList.swift rename to LocalPackage/Sources/HometeDomain/Cohabitant/Housework/HouseworkBoardList.swift index abbf5571..7910eb9b 100644 --- a/homete/Model/Domain/Cohabitant/Housework/HouseworkBoardList.swift +++ b/LocalPackage/Sources/HometeDomain/Cohabitant/Housework/HouseworkBoardList.swift @@ -7,24 +7,28 @@ import Foundation -struct HouseworkBoardList: Equatable { - - private(set) var items: [HouseworkItem] - - func items(matching state: HouseworkState) -> [HouseworkItem] { +public struct HouseworkBoardList: Equatable { + + public private(set) var items: [HouseworkItem] + + public func items(matching state: HouseworkState) -> [HouseworkItem] { print("HouseworkBoardList filtering(state: \(state), items: \(items))") return items.filter { $0.state == state } } + + public init(items: [HouseworkItem]) { + self.items = items + } } -extension HouseworkBoardList { - +public extension HouseworkBoardList { + init( dailyList: [DailyHouseworkList], selectedDate: Date, calendar: Calendar ) { - + items = dailyList .first { $0.metaData.indexedDate == .init(selectedDate, calendar: calendar) diff --git a/homete/Model/Domain/Cohabitant/Housework/HouseworkHistoryList.swift b/LocalPackage/Sources/HometeDomain/Cohabitant/Housework/HouseworkHistoryList.swift similarity index 76% rename from homete/Model/Domain/Cohabitant/Housework/HouseworkHistoryList.swift rename to LocalPackage/Sources/HometeDomain/Cohabitant/Housework/HouseworkHistoryList.swift index 567d0d50..485dd5e9 100644 --- a/homete/Model/Domain/Cohabitant/Housework/HouseworkHistoryList.swift +++ b/LocalPackage/Sources/HometeDomain/Cohabitant/Housework/HouseworkHistoryList.swift @@ -7,30 +7,34 @@ import Foundation -struct HouseworkHistoryList: Equatable { - - private(set) var items: [String] - +public struct HouseworkHistoryList: Equatable { + + public private(set) var items: [String] + + public init(items: [String]) { + self.items = items + } + /// 履歴があるかどうか - var hasHistory: Bool { !items.isEmpty } - + public var hasHistory: Bool { !items.isEmpty } + /// 引数に受け取った文字列が `items` に存在する場合、その要素を先頭へ移動します。 /// - Parameter value: 先頭へ移動したい要素の文字列 - mutating func moveToFrontIfExists(_ value: String) { - + public mutating func moveToFrontIfExists(_ value: String) { + guard let index = items.firstIndex(of: value) else { return } // 既に先頭なら何もしない if index == items.startIndex { return } let element = items.remove(at: index) items.insert(element, at: 0) } - + /// 引数に受け取った文字列を `items`の先頭に追加する /// - Parameter value: 新しい履歴文字 - mutating func addNewHistory(_ value: String) { - + public mutating func addNewHistory(_ value: String) { + guard items.contains(value) else { - + items.insert(value, at: 0) return } @@ -39,40 +43,40 @@ struct HouseworkHistoryList: Equatable { } extension HouseworkHistoryList: Codable { - + enum CodingKeys: String, CodingKey { case items } - - func encode(to encoder: any Encoder) throws { + + public func encode(to encoder: any Encoder) throws { var container = encoder.container(keyedBy: CodingKeys.self) try container.encode(items, forKey: .items) } - - init(from decoder: any Decoder) throws { + + public init(from decoder: any Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) items = try container.decode(Array.self, forKey: .items) } } extension HouseworkHistoryList: RawRepresentable { - - init?(rawValue: String) { - + + public init?(rawValue: String) { + guard let data = rawValue.data(using: .utf8), let decoded = try? JSONDecoder().decode(HouseworkHistoryList.self, from: data) else { - + return nil } self = decoded } - - var rawValue: String { - + + public var rawValue: String { + guard let data = try? JSONEncoder().encode(self), let jsonString = String(data: data, encoding: .utf8) else { - + return "" } return jsonString diff --git a/homete/Model/Domain/Cohabitant/Housework/HouseworkIndexedDate.swift b/LocalPackage/Sources/HometeDomain/Cohabitant/Housework/HouseworkIndexedDate.swift similarity index 81% rename from homete/Model/Domain/Cohabitant/Housework/HouseworkIndexedDate.swift rename to LocalPackage/Sources/HometeDomain/Cohabitant/Housework/HouseworkIndexedDate.swift index 89756700..05fd06a3 100644 --- a/homete/Model/Domain/Cohabitant/Housework/HouseworkIndexedDate.swift +++ b/LocalPackage/Sources/HometeDomain/Cohabitant/Housework/HouseworkIndexedDate.swift @@ -7,32 +7,36 @@ import Foundation -struct HouseworkIndexedDate: Equatable, Codable, Hashable { - - let value: String - - static func calcTargetPeriod( +public struct HouseworkIndexedDate: Equatable, Codable, Hashable, Sendable { + + public let value: String + + public init(value: String) { + self.value = value + } + + public static func calcTargetPeriod( anchorDate: Date, offsetDays: Int, calendar: Calendar ) -> [[String: String]] { - + let base = calendar.startOfDay(for: anchorDate) guard offsetDays >= 0 else { - + return [["value": HouseworkIndexedDate(base, calendar: calendar).value]] } // -offset ... +offset の範囲を列挙 return (-offsetDays...offsetDays).compactMap { delta in - + guard let date = calendar.date(byAdding: .day, value: delta, to: base) else { return nil } return ["value": HouseworkIndexedDate(date, calendar: calendar).value] } } } -extension HouseworkIndexedDate { - +public extension HouseworkIndexedDate { + init(_ date: Date, calendar: Calendar) { let formatStyle = Date.FormatStyle( date: .numeric, diff --git a/homete/Model/Domain/Cohabitant/Housework/HouseworkItem.swift b/LocalPackage/Sources/HometeDomain/Cohabitant/Housework/HouseworkItem.swift similarity index 63% rename from homete/Model/Domain/Cohabitant/Housework/HouseworkItem.swift rename to LocalPackage/Sources/HometeDomain/Cohabitant/Housework/HouseworkItem.swift index 81310b69..c2d09522 100644 --- a/homete/Model/Domain/Cohabitant/Housework/HouseworkItem.swift +++ b/LocalPackage/Sources/HometeDomain/Cohabitant/Housework/HouseworkItem.swift @@ -7,43 +7,69 @@ import Foundation -struct HouseworkItem: Identifiable, Equatable, Sendable, Hashable, Codable { - - let id: String +public struct HouseworkItem: Identifiable, Equatable, Sendable, Hashable, Codable { + + public let id: String /// 家事の日付情報 - let indexedDate: HouseworkIndexedDate + public let indexedDate: HouseworkIndexedDate /// 家事のタイトル - let title: String + public let title: String /// 家事ポイント - let point: Int + public let point: Int /// 家事ステータス - let state: HouseworkState + public let state: HouseworkState /// 実行者のユーザID - let executorId: String? + public let executorId: String? /// 実行日時 - let executedAt: Date? + public let executedAt: Date? /// 確認者のユーザID - let reviewerId: String? + public let reviewerId: String? /// 承認日時 - let approvedAt: Date? + public let approvedAt: Date? /// 確認コメント - let reviewerComment: String? + public let reviewerComment: String? /// 有効期限 - let expiredAt: Date - - var formattedIndexedDate: String { - + public let expiredAt: Date + + public init( + id: String, + indexedDate: HouseworkIndexedDate, + title: String, + point: Int, + state: HouseworkState, + executorId: String?, + executedAt: Date?, + reviewerId: String?, + approvedAt: Date?, + reviewerComment: String?, + expiredAt: Date + ) { + self.id = id + self.indexedDate = indexedDate + self.title = title + self.point = point + self.state = state + self.executorId = executorId + self.executedAt = executedAt + self.reviewerId = reviewerId + self.approvedAt = approvedAt + self.reviewerComment = reviewerComment + self.expiredAt = expiredAt + } + + public var formattedIndexedDate: String { + return indexedDate.value } - + /// レビュー可能かどうか - func canReview(ownUserId: String) -> Bool { - + public func canReview(ownUserId: String) -> Bool { + return executorId != ownUserId && state != .completed } - - func updatePendingApproval(at now: Date, changer: String) -> Self { - + + public func updatePendingApproval(at now: Date, changer: String) -> Self { + return .init( id: id, indexedDate: indexedDate, @@ -58,9 +84,9 @@ struct HouseworkItem: Identifiable, Equatable, Sendable, Hashable, Codable { expiredAt: expiredAt ) } - - func updateApproved(at now: Date, reviewer: String, comment: String) -> Self { - + + public func updateApproved(at now: Date, reviewer: String, comment: String) -> Self { + return .init( id: id, indexedDate: indexedDate, @@ -75,9 +101,9 @@ struct HouseworkItem: Identifiable, Equatable, Sendable, Hashable, Codable { expiredAt: expiredAt ) } - - func updateRejected(at now: Date, reviewer: String, comment: String) -> Self { - + + public func updateRejected(at now: Date, reviewer: String, comment: String) -> Self { + return .init( id: id, indexedDate: indexedDate, @@ -92,9 +118,9 @@ struct HouseworkItem: Identifiable, Equatable, Sendable, Hashable, Codable { expiredAt: expiredAt ) } - - func updateIncomplete() -> Self { - + + public func updateIncomplete() -> Self { + return .init( id: id, indexedDate: indexedDate, @@ -111,8 +137,8 @@ struct HouseworkItem: Identifiable, Equatable, Sendable, Hashable, Codable { } } -extension HouseworkItem { - +public extension HouseworkItem { + init( id: String, title: String, @@ -125,7 +151,7 @@ extension HouseworkItem { approvedAt: Date? = nil, reviewerComment: String? = nil ) { - + self.init( id: id, indexedDate: metaData.indexedDate, diff --git a/homete/Model/Domain/Cohabitant/Housework/HouseworkListStore.swift b/LocalPackage/Sources/HometeDomain/Cohabitant/Housework/HouseworkListStore.swift similarity index 77% rename from homete/Model/Domain/Cohabitant/Housework/HouseworkListStore.swift rename to LocalPackage/Sources/HometeDomain/Cohabitant/Housework/HouseworkListStore.swift index 0675b857..d564c152 100644 --- a/homete/Model/Domain/Cohabitant/Housework/HouseworkListStore.swift +++ b/LocalPackage/Sources/HometeDomain/Cohabitant/Housework/HouseworkListStore.swift @@ -5,56 +5,60 @@ // Created by 佐藤汰一 on 2025/09/27. // -import SwiftUI +import Foundation +import Observation @MainActor @Observable -final class HouseworkListStore { - - private(set) var items: StoredAllHouseworkList +public final class HouseworkListStore { + + public private(set) var items: StoredAllHouseworkList private var cohabitantId: String - + + private var observeTask: Task? + private let houseworkClient: HouseworkClient private let cohabitantPushNotificationClient: CohabitantPushNotificationClient - + private let houseworkObserveKey = "houseworkObserveKey" - - init( + + public init( houseworkClient: HouseworkClient = .previewValue, cohabitantPushNotificationClient: CohabitantPushNotificationClient = .previewValue, items: [DailyHouseworkList] = [], cohabitantId: String = "" ) { - + self.houseworkClient = houseworkClient self.cohabitantPushNotificationClient = cohabitantPushNotificationClient self.items = .init(value: items) self.cohabitantId = cohabitantId } - - func loadHouseworkList(currentTime: Date, cohabitantId: String, calendar: Calendar) async { - + + public func loadHouseworkList(currentTime: Date, cohabitantId: String, calendar: Calendar) async { + self.cohabitantId = cohabitantId - + guard !cohabitantId.isEmpty else { - + await clear() return } - + + observeTask?.cancel() await houseworkClient.removeListener(houseworkObserveKey) - + let houseworkListStream = await houseworkClient.snapshotListener( houseworkObserveKey, cohabitantId, currentTime, 3 ) - - Task { - + + observeTask = Task { + for await currentItems in houseworkListStream { - + items = StoredAllHouseworkList.makeMultiDateList( items: currentItems, calendar: calendar @@ -62,19 +66,19 @@ final class HouseworkListStore { } } } - - func register(_ newItem: HouseworkItem) async throws { - + + public func register(_ newItem: HouseworkItem) async throws { + try await houseworkClient.insertOrUpdateItem(newItem, cohabitantId) - + Task.detached { - + let notificationContent = PushNotificationContent.addNewHouseworkItem(newItem.title) try await self.cohabitantPushNotificationClient.send(self.cohabitantId, notificationContent) } } - - func requestReview(target: HouseworkItem, now: Date, executor: String) async throws { + + public func requestReview(target: HouseworkItem, now: Date, executor: String) async throws { try await updateAndSave(target: target) { $0.updatePendingApproval(at: now, changer: executor) @@ -82,8 +86,8 @@ final class HouseworkListStore { .requestReviewMessage(houseworkTitle: target.title) } } - - func approved(target: HouseworkItem, now: Date, reviwer: Account, comment: String) async throws { + + public func approved(target: HouseworkItem, now: Date, reviwer: Account, comment: String) async throws { try await updateAndSave(target: target) { $0.updateApproved(at: now, reviewer: reviwer.id, comment: comment) @@ -92,7 +96,7 @@ final class HouseworkListStore { } } - func rejected(target: HouseworkItem, now: Date, reviwer: Account, comment: String) async throws { + public func rejected(target: HouseworkItem, now: Date, reviwer: Account, comment: String) async throws { try await updateAndSave(target: target) { $0.updateRejected(at: now, reviewer: reviwer.id, comment: comment) @@ -101,23 +105,31 @@ final class HouseworkListStore { } } - func returnToIncomplete(target: HouseworkItem) async throws { + public func returnToIncomplete(target: HouseworkItem) async throws { try await updateAndSave(target: target) { $0.updateIncomplete() } } - - func remove(_ target: HouseworkItem) async throws { - + + public func remove(_ target: HouseworkItem) async throws { + try await houseworkClient.removeItem(target, cohabitantId) } + + public func stopObserving() async { + + observeTask?.cancel() + await observeTask?.value + observeTask = nil + } } private extension HouseworkListStore { func clear() async { + await stopObserving() await houseworkClient.removeListener(houseworkObserveKey) items.removeAll() } diff --git a/homete/Model/Domain/Cohabitant/Housework/HouseworkState.swift b/LocalPackage/Sources/HometeDomain/Cohabitant/Housework/HouseworkState.swift similarity index 58% rename from homete/Model/Domain/Cohabitant/Housework/HouseworkState.swift rename to LocalPackage/Sources/HometeDomain/Cohabitant/Housework/HouseworkState.swift index 3ed3278d..176b1b1f 100644 --- a/homete/Model/Domain/Cohabitant/Housework/HouseworkState.swift +++ b/LocalPackage/Sources/HometeDomain/Cohabitant/Housework/HouseworkState.swift @@ -5,11 +5,11 @@ // Created by 佐藤汰一 on 2025/09/06. // -enum HouseworkState: CaseIterable, Identifiable, Codable, Sendable { - +public enum HouseworkState: CaseIterable, Identifiable, Codable, Sendable { + case incomplete case pendingApproval case completed - - var id: Self { self } + + public var id: Self { self } } diff --git a/homete/Model/Domain/Cohabitant/Housework/StoredAllHouseworkList.swift b/LocalPackage/Sources/HometeDomain/Cohabitant/Housework/StoredAllHouseworkList.swift similarity index 67% rename from homete/Model/Domain/Cohabitant/Housework/StoredAllHouseworkList.swift rename to LocalPackage/Sources/HometeDomain/Cohabitant/Housework/StoredAllHouseworkList.swift index 0758c3bc..50c7a737 100644 --- a/homete/Model/Domain/Cohabitant/Housework/StoredAllHouseworkList.swift +++ b/LocalPackage/Sources/HometeDomain/Cohabitant/Housework/StoredAllHouseworkList.swift @@ -7,15 +7,19 @@ import Foundation -struct StoredAllHouseworkList: Equatable, Sendable { - - private(set) var value: [DailyHouseworkList] - - static func makeMultiDateList(items: [HouseworkItem], calendar: Calendar) -> Self { - +public struct StoredAllHouseworkList: Equatable, Sendable { + + public private(set) var value: [DailyHouseworkList] + + public init(value: [DailyHouseworkList]) { + self.value = value + } + + public static func makeMultiDateList(items: [HouseworkItem], calendar: Calendar) -> Self { + let items: [DailyHouseworkList] = Dictionary(grouping: items) { $0.formattedIndexedDate } .compactMap { - + guard let firstItem = $1.first else { return nil } return .init( items: $1, @@ -24,17 +28,17 @@ struct StoredAllHouseworkList: Equatable, Sendable { } return .init(value: items) } - - func item(_ item: HouseworkItem) -> HouseworkItem? { - + + public func item(_ item: HouseworkItem) -> HouseworkItem? { + guard let targetDayList = value.first( where: { $0.metaData.indexedDate == item.indexedDate } ), let targetItem = targetDayList.items.first(where: { $0.id == item.id }) else { return nil } return targetItem } - - mutating func removeAll() { + + public mutating func removeAll() { value = [] } } diff --git a/LocalPackage/Sources/HometeDomain/Dependencies/AccountAuthClient.swift b/LocalPackage/Sources/HometeDomain/Dependencies/AccountAuthClient.swift new file mode 100644 index 00000000..3ea5fc8d --- /dev/null +++ b/LocalPackage/Sources/HometeDomain/Dependencies/AccountAuthClient.swift @@ -0,0 +1,45 @@ +// +// AccountListener.swift +// homete +// +// Created by 佐藤汰一 on 2025/08/03. +// + +import Foundation + +public struct AccountAuthClient: Sendable { + + public let signIn: @Sendable (String, String) async throws -> AccountAuthResult + public let signOut: @Sendable () throws -> Void + public let makeListener: @Sendable () -> AccountListenerStream + public let reauthenticateWithApple: @Sendable (_ signInWithAppleResult: SignInWithAppleResult) async throws -> Void + public let revokeAppleToken: @Sendable (_ authorizationCode: String) async throws -> Void + public let deleteAccount: @Sendable () async throws -> Void + + public init( + signIn: @Sendable @escaping (String, String) async throws -> AccountAuthResult = { _, _ in .init(id: "id") }, + signOut: @Sendable @escaping () throws -> Void = {}, + makeListener: @Sendable @escaping () -> AccountListenerStream = { + + let (stream, continuation) = AsyncStream.makeStream() + return AccountListenerStream(values: stream, + listenerToken: NSObject(), + continuation: continuation) + }, + reauthenticateWithApple: @Sendable @escaping (_: SignInWithAppleResult) async throws -> Void = { _ in }, + revokeAppleToken: @Sendable @escaping (_: String) async throws -> Void = { _ in }, + deleteAccount: @Sendable @escaping () async throws -> Void = {} + ) { + + self.signIn = signIn + self.signOut = signOut + self.makeListener = makeListener + self.reauthenticateWithApple = reauthenticateWithApple + self.revokeAppleToken = revokeAppleToken + self.deleteAccount = deleteAccount + } +} + +public extension AccountAuthClient { + static let previewValue = AccountAuthClient() +} diff --git a/LocalPackage/Sources/HometeDomain/Dependencies/AccountInfoClient.swift b/LocalPackage/Sources/HometeDomain/Dependencies/AccountInfoClient.swift new file mode 100644 index 00000000..e55da1d3 --- /dev/null +++ b/LocalPackage/Sources/HometeDomain/Dependencies/AccountInfoClient.swift @@ -0,0 +1,25 @@ +// +// AccountInfoClient.swift +// homete +// +// Created by 佐藤汰一 on 2025/08/09. +// + +public struct AccountInfoClient: Sendable { + + public let insertOrUpdate: @Sendable (Account) async throws -> Void + public let fetch: @Sendable (String) async throws -> Account? + + public init( + insertOrUpdate: @Sendable @escaping (Account) async throws -> Void = { _ in }, + fetch: @Sendable @escaping (String) async throws -> Account? = { _ in nil } + ) { + self.insertOrUpdate = insertOrUpdate + self.fetch = fetch + } +} + +public extension AccountInfoClient { + + static let previewValue: AccountInfoClient = .init() +} diff --git a/LocalPackage/Sources/HometeDomain/Dependencies/AnalyticsClient.swift b/LocalPackage/Sources/HometeDomain/Dependencies/AnalyticsClient.swift new file mode 100644 index 00000000..78e63773 --- /dev/null +++ b/LocalPackage/Sources/HometeDomain/Dependencies/AnalyticsClient.swift @@ -0,0 +1,26 @@ +// +// AnalyticsClient.swift +// homete +// +// Created by 佐藤汰一 on 2025/08/09. +// + +public struct AnalyticsClient: Sendable { + + public let setId: @Sendable (String) -> Void + public let log: @Sendable (AnalyticsEvent) -> Void + + public init( + setId: @Sendable @escaping (String) -> Void = { _ in }, + log: @Sendable @escaping (AnalyticsEvent) -> Void = { _ in } + ) { + + self.setId = setId + self.log = log + } +} + +public extension AnalyticsClient { + + static let previewValue = AnalyticsClient() +} diff --git a/LocalPackage/Sources/HometeDomain/Dependencies/CohabitantClient.swift b/LocalPackage/Sources/HometeDomain/Dependencies/CohabitantClient.swift new file mode 100644 index 00000000..f35d8629 --- /dev/null +++ b/LocalPackage/Sources/HometeDomain/Dependencies/CohabitantClient.swift @@ -0,0 +1,35 @@ +// +// CohabitantClient.swift +// homete +// +// Created by 佐藤汰一 on 2025/08/18. +// + +public struct CohabitantClient: Sendable { + + public let register: @Sendable (CohabitantData) async throws -> Void + public let addSnapshotListener: @Sendable ( + _ listenerId: String, + _ cohabitantId: String + ) async -> AsyncStream<[CohabitantData]> + public let removeSnapshotListener: @Sendable (_ listenerId: String) async -> Void + + public init( + register: @Sendable @escaping (CohabitantData) async throws -> Void = { _ in }, + addSnapshotListener: @Sendable @escaping ( + _: String, + _: String + ) async -> AsyncStream<[CohabitantData]> = { _, _ in .init { nil } }, + removeSnapshotListener: @Sendable @escaping (_ listenerId: String) async -> Void = { _ in } + ) { + + self.register = register + self.addSnapshotListener = addSnapshotListener + self.removeSnapshotListener = removeSnapshotListener + } +} + +public extension CohabitantClient { + + static let previewValue: CohabitantClient = .init() +} diff --git a/LocalPackage/Sources/HometeDomain/Dependencies/CohabitantPushNotificationClient.swift b/LocalPackage/Sources/HometeDomain/Dependencies/CohabitantPushNotificationClient.swift new file mode 100644 index 00000000..45418c88 --- /dev/null +++ b/LocalPackage/Sources/HometeDomain/Dependencies/CohabitantPushNotificationClient.swift @@ -0,0 +1,19 @@ +// +// CohabitantPushNotificationClient.swift +// homete +// +// Created by 佐藤汰一 on 2025/11/12. +// + +public struct CohabitantPushNotificationClient: Sendable { + public let send: @Sendable (_ id: String, _ content: PushNotificationContent) async throws -> Void + + public init(send: @Sendable @escaping (_ id: String, _ content: PushNotificationContent) async throws -> Void) { + self.send = send + } +} + +public extension CohabitantPushNotificationClient { + + static let previewValue: CohabitantPushNotificationClient = .init { _, _ in } +} diff --git a/homete/Model/Dependencies/DependencyClient.swift b/LocalPackage/Sources/HometeDomain/Dependencies/DependencyClient.swift similarity index 79% rename from homete/Model/Dependencies/DependencyClient.swift rename to LocalPackage/Sources/HometeDomain/Dependencies/DependencyClient.swift index d5205b35..1145595a 100644 --- a/homete/Model/Dependencies/DependencyClient.swift +++ b/LocalPackage/Sources/HometeDomain/Dependencies/DependencyClient.swift @@ -5,8 +5,8 @@ // Created by 佐藤汰一 on 2025/08/04. // -protocol DependencyClient: Sendable { - +public protocol DependencyClient: Sendable { + static var liveValue: Self { get } static var previewValue: Self { get } } diff --git a/LocalPackage/Sources/HometeDomain/Dependencies/HouseworkClient.swift b/LocalPackage/Sources/HometeDomain/Dependencies/HouseworkClient.swift new file mode 100644 index 00000000..9251b46e --- /dev/null +++ b/LocalPackage/Sources/HometeDomain/Dependencies/HouseworkClient.swift @@ -0,0 +1,50 @@ +// +// HouseworkClient.swift +// homete +// +// Created by 佐藤汰一 on 2025/09/07. +// + +import Foundation + +public struct HouseworkClient: Sendable { + + public let insertOrUpdateItem: @Sendable (_ item: HouseworkItem, _ cohabitantId: String) async throws -> Void + public let removeItem: @Sendable (_ item: HouseworkItem, _ cohabitantId: String) async throws -> Void + public let snapshotListener: @Sendable ( + _ id: String, + _ cohabitantId: String, + _ anchorDate: Date, + _ offset: Int + ) async -> AsyncStream<[HouseworkItem]> + public let removeListener: @Sendable (_ id: String) async -> Void +} + +public extension HouseworkClient { + + init( + insertOrUpdateItemHandler: @escaping @Sendable ( + _ item: HouseworkItem, + _ cohabitantId: String + ) async throws -> Void = { _, _ in }, + removeItemHandler: @escaping @Sendable ( + _ item: HouseworkItem, + _ cohabitantId: String + ) async throws -> Void = { _, _ in }, + snapshotListenerHandler: @escaping @Sendable ( + _ id: String, + _ cohabitantId: String, + _ anchorDate: Date, + _ offset: Int + ) async -> AsyncStream<[HouseworkItem]> = { _, _, _, _ in .makeStream().stream }, + removeListenerHandler: @escaping @Sendable (_ id: String) async -> Void = { _ in } + ) { + + insertOrUpdateItem = insertOrUpdateItemHandler + removeItem = removeItemHandler + snapshotListener = snapshotListenerHandler + removeListener = removeListenerHandler + } + + static let previewValue = HouseworkClient() +} diff --git a/LocalPackage/Sources/HometeDomain/Dependencies/NonceGenerationClient.swift b/LocalPackage/Sources/HometeDomain/Dependencies/NonceGenerationClient.swift new file mode 100644 index 00000000..43594936 --- /dev/null +++ b/LocalPackage/Sources/HometeDomain/Dependencies/NonceGenerationClient.swift @@ -0,0 +1,29 @@ +// +// NonceGenerationClient.swift +// homete +// +// Created by 佐藤汰一 on 2025/08/03. +// + +import Foundation + +public struct NonceGenerationClient: Sendable { + + public let value: @Sendable () -> SignInWithAppleNonce + public func callAsFunction() -> SignInWithAppleNonce { + + return value() + } + + public init(value: @Sendable @escaping () -> SignInWithAppleNonce) { + self.value = value + } +} + +public extension NonceGenerationClient { + + static let previewValue: NonceGenerationClient = .init { + + return .init(original: "preview", sha256: "preview sha256") + } +} diff --git a/LocalPackage/Sources/HometeDomain/Dependencies/SignInWithAppleClient.swift b/LocalPackage/Sources/HometeDomain/Dependencies/SignInWithAppleClient.swift new file mode 100644 index 00000000..c5db031d --- /dev/null +++ b/LocalPackage/Sources/HometeDomain/Dependencies/SignInWithAppleClient.swift @@ -0,0 +1,23 @@ +// +// SignInWithAppleClient.swift +// homete +// +// Created by 佐藤汰一 on 2025/12/31. +// + +public struct SignInWithAppleClient: Sendable { + public let reauthentication: @MainActor (_ nonce: SignInWithAppleNonce) async throws -> SignInWithAppleResult + + public init( + reauthentication: @MainActor @escaping (_ nonce: SignInWithAppleNonce) + async throws -> SignInWithAppleResult = { _ in preconditionFailure() } + ) { + + self.reauthentication = reauthentication + } +} + +public extension SignInWithAppleClient { + + static let previewValue = SignInWithAppleClient() +} diff --git a/homete/Model/Domain/DomainError.swift b/LocalPackage/Sources/HometeDomain/DomainError.swift similarity index 77% rename from homete/Model/Domain/DomainError.swift rename to LocalPackage/Sources/HometeDomain/DomainError.swift index b8668556..5430daaf 100644 --- a/homete/Model/Domain/DomainError.swift +++ b/LocalPackage/Sources/HometeDomain/DomainError.swift @@ -5,17 +5,17 @@ // Created by 佐藤汰一 on 2025/08/09. // -enum DomainError: Error, Equatable { - +public enum DomainError: Error, Equatable, Sendable { + case failAuth case noNetwork case other } -extension DomainError { - +public extension DomainError { + static func make(_ error: (any Error)?) -> Self? { - + guard let error else { return nil } return error as? DomainError ?? .other } diff --git a/homete/Model/Domain/PushNotificationContent.swift b/LocalPackage/Sources/HometeDomain/PushNotificationContent.swift similarity index 79% rename from homete/Model/Domain/PushNotificationContent.swift rename to LocalPackage/Sources/HometeDomain/PushNotificationContent.swift index d8a01041..c6d6e3be 100644 --- a/homete/Model/Domain/PushNotificationContent.swift +++ b/LocalPackage/Sources/HometeDomain/PushNotificationContent.swift @@ -5,12 +5,17 @@ // Created by 佐藤汰一 on 2025/11/12. // -struct PushNotificationContent: Equatable { - let title: String - let message: String +public struct PushNotificationContent: Equatable, Sendable { + public let title: String + public let message: String + + public init(title: String, message: String) { + self.title = title + self.message = message + } } -extension PushNotificationContent { +public extension PushNotificationContent { static func addNewHouseworkItem(_ houseworkTitle: String) -> Self { return .init( diff --git a/homete/Model/Domain/Setting/SettingMenuItem.swift b/LocalPackage/Sources/HometeDomain/Setting/SettingMenuItem.swift similarity index 75% rename from homete/Model/Domain/Setting/SettingMenuItem.swift rename to LocalPackage/Sources/HometeDomain/Setting/SettingMenuItem.swift index a1c7f035..6fc12f43 100644 --- a/homete/Model/Domain/Setting/SettingMenuItem.swift +++ b/LocalPackage/Sources/HometeDomain/Setting/SettingMenuItem.swift @@ -5,37 +5,41 @@ // Created by 佐藤汰一 on 2025/08/11. // +#if canImport(SwiftUI) import SwiftUI +#endif + +public enum SettingMenuItem: Equatable, CaseIterable { -enum SettingMenuItem: Equatable, CaseIterable { - case taskTemplate case privacyPolicy case license - - var title: LocalizedStringKey { - + + #if canImport(SwiftUI) + public var title: LocalizedStringKey { + switch self { case .taskTemplate: return "家事テンプレート" - + case .privacyPolicy: return "プライバシーポリシー" - + case .license: return "ライセンス" } } - - var iconName: String { - + #endif + + public var iconName: String { + switch self { case .taskTemplate: return "house" - + case .privacyPolicy: return "hand.raised" - + case .license: return "cube.box" } diff --git a/LocalPackage/Sources/LocalPackage/LocalPackage.swift b/LocalPackage/Sources/LocalPackage/LocalPackage.swift deleted file mode 100644 index 08b22b80..00000000 --- a/LocalPackage/Sources/LocalPackage/LocalPackage.swift +++ /dev/null @@ -1,2 +0,0 @@ -// The Swift Programming Language -// https://docs.swift.org/swift-book diff --git a/hometeTests/Domain/AccountAuthStoreTest.swift b/LocalPackage/Tests/HometeDomainTests/AccountAuthStoreTest.swift similarity index 61% rename from hometeTests/Domain/AccountAuthStoreTest.swift rename to LocalPackage/Tests/HometeDomainTests/AccountAuthStoreTest.swift index 231b6c23..4e832b53 100644 --- a/hometeTests/Domain/AccountAuthStoreTest.swift +++ b/LocalPackage/Tests/HometeDomainTests/AccountAuthStoreTest.swift @@ -5,9 +5,8 @@ // Created by 佐藤汰一 on 2025/08/09. // -import os import Testing -@testable import homete +@testable import HometeDomain @MainActor struct AccountAuthStoreTest { @@ -21,7 +20,7 @@ struct AccountAuthStoreTest { try await confirmation(expectedCount: 4) { confirmation in - let store = AccountAuthStore(appDependencies: .init( + let store = AccountAuthStore( accountAuthClient: .init( signIn: { tokenId, nonce in @@ -48,49 +47,49 @@ struct AccountAuthStoreTest { #expect(event == .login(isSuccess: true)) } ) - )) + ) try await store.login(.init(tokenId: inputTokenId, nonce: inputNonce, authorizationCode: "code")) } } @Test("ログアウト時はローカルのログイン状態をログアウトにしてログアウト処理を行い、イベントログを送信する") - func test_logout() throws { - - let isCallSignOut = OSAllocatedUnfairLock(initialState: false) - let isCallAnalyticsLog = OSAllocatedUnfairLock(initialState: false) - - let store = AccountAuthStore( - appDependencies: .init( + func test_logout() async throws { + + try await confirmation(expectedCount: 3) { confirmation in + + let store = AccountAuthStore( accountAuthClient: .init( signIn: { _, _ in - + Issue.record() return .init(id: "") }, - signOut: { isCallSignOut.withLock { $0 = true } }, - makeListener: { .defaultValue() } + signOut: { confirmation() }, + makeListener: { + + confirmation() + return .defaultValue() + } ), analyticsClient: .init( setId: { _ in - + Issue.record() }, log: { event in - - isCallAnalyticsLog.withLock { $0 = true } + + confirmation() #expect(event == .logout()) } - ) - ), - currentAuth: .init(result: .init(id: "test"), alreadyLoadedAtInitiate: true) - ) - - store.logOut() - - #expect(isCallSignOut.withLock { $0 }) - #expect(isCallAnalyticsLog.withLock { $0 }) - #expect(store.currentAuth == .init(result: nil, alreadyLoadedAtInitiate: true)) + ), + currentAuth: .init(result: .init(id: "test"), alreadyLoadedAtInitiate: true) + ) + + store.logOut() + + #expect(store.currentAuth == .init(result: nil, alreadyLoadedAtInitiate: true)) + } } @Test("再認証を行った後にアカウントを削除し認証トークンの無効化を行いログアウト状態にすることで、退会処理を完了させる") @@ -107,42 +106,40 @@ struct AccountAuthStoreTest { try await confirmation(expectedCount: 6) { confirmation in let store = AccountAuthStore( - appDependencies: .init( - nonceGeneratorClient: .init { + accountAuthClient: .init( + reauthenticateWithApple: { result in confirmation() - return inputNonce + #expect(result == inputSignInWithAppleResult) }, - accountAuthClient: .init( - reauthenticateWithApple: { result in - - confirmation() - #expect(result == inputSignInWithAppleResult) - }, - revokeAppleToken: { code in - - confirmation() - #expect(code == inputSignInWithAppleResult.authorizationCode) - }, - deleteAccount: { - - confirmation() - }, - ), - analyticsClient: .init( - log: { event in - - confirmation() - #expect(event == .deleteAccount()) - } - ), - signInWithAppleClient: .init { nonce in + revokeAppleToken: { code in confirmation() - #expect(nonce == inputNonce) - return inputSignInWithAppleResult + #expect(code == inputSignInWithAppleResult.authorizationCode) + }, + deleteAccount: { + + confirmation() + }, + ), + analyticsClient: .init( + log: { event in + + confirmation() + #expect(event == .deleteAccount()) } ), + signInWithAppleClient: .init { nonce in + + confirmation() + #expect(nonce == inputNonce) + return inputSignInWithAppleResult + }, + nonceGenerationClient: .init { + + confirmation() + return inputNonce + }, currentAuth: .init(result: .init(id: "test"), alreadyLoadedAtInitiate: true) ) diff --git a/hometeTests/Domain/AccountStoreTest.swift b/LocalPackage/Tests/HometeDomainTests/AccountStoreTest.swift similarity index 92% rename from hometeTests/Domain/AccountStoreTest.swift rename to LocalPackage/Tests/HometeDomainTests/AccountStoreTest.swift index e5038694..81cb2beb 100644 --- a/hometeTests/Domain/AccountStoreTest.swift +++ b/LocalPackage/Tests/HometeDomainTests/AccountStoreTest.swift @@ -6,7 +6,7 @@ // import Testing -@testable import homete +@testable import HometeDomain @MainActor struct AccountStoreTest { @@ -32,7 +32,7 @@ struct AccountStoreTest { #expect($0 == inputAuthResult.id) return inputAccount }) - let store = AccountStore(appDependencies: .init(accountInfoClient: accountInfoClient)) + let store = AccountStore(accountInfoClient: accountInfoClient) // Act let actual = await store.load(inputAuthResult) @@ -62,7 +62,7 @@ struct AccountStoreTest { #expect($0 == expectedAccount) }) let store = AccountStore( - appDependencies: .init(accountInfoClient: accountInfoClient), + accountInfoClient: accountInfoClient, account: initialAccount ) @@ -99,7 +99,7 @@ struct AccountStoreTest { #expect($0 == expectedAccount) }) let store = AccountStore( - appDependencies: .init(accountInfoClient: accountInfoClient), + accountInfoClient: accountInfoClient, account: initialAccount ) diff --git a/hometeTests/Domain/CohabitantRegistration/CohabitantRegistrationMessageTests.swift b/LocalPackage/Tests/HometeDomainTests/CohabitantRegistration/CohabitantRegistrationMessageTests.swift similarity index 99% rename from hometeTests/Domain/CohabitantRegistration/CohabitantRegistrationMessageTests.swift rename to LocalPackage/Tests/HometeDomainTests/CohabitantRegistration/CohabitantRegistrationMessageTests.swift index d55600f2..79a37684 100644 --- a/hometeTests/Domain/CohabitantRegistration/CohabitantRegistrationMessageTests.swift +++ b/LocalPackage/Tests/HometeDomainTests/CohabitantRegistration/CohabitantRegistrationMessageTests.swift @@ -7,7 +7,7 @@ import Foundation import Testing -@testable import homete +@testable import HometeDomain struct CohabitantRegistrationMessageTests { diff --git a/hometeTests/Domain/CohabitantStoreTest.swift b/LocalPackage/Tests/HometeDomainTests/CohabitantStoreTest.swift similarity index 69% rename from hometeTests/Domain/CohabitantStoreTest.swift rename to LocalPackage/Tests/HometeDomainTests/CohabitantStoreTest.swift index a94bea94..88919205 100644 --- a/hometeTests/Domain/CohabitantStoreTest.swift +++ b/LocalPackage/Tests/HometeDomainTests/CohabitantStoreTest.swift @@ -7,7 +7,7 @@ import Testing import Observation -@testable import homete +@testable import HometeDomain @MainActor struct CohabitantStoreTest { @@ -36,23 +36,21 @@ struct CohabitantStoreTest { let (stream, continuation) = AsyncStream<[CohabitantData]>.makeStream() let store = CohabitantStore( - appDependencies: .init( - accountInfoClient: .init(fetch: { userId in - - // Assert - - #expect(userId == newMemberId) - return expectedAccount - }), - cohabitantClient: .init( - addSnapshotListener: { listenerId, cohabitantId in - - #expect(listenerId == inputListenerId) - #expect(cohabitantId == inputCohabitantId) - return stream - } - ) - ) + cohabitantClient: .init( + addSnapshotListener: { listenerId, cohabitantId in + + #expect(listenerId == inputListenerId) + #expect(cohabitantId == inputCohabitantId) + return stream + } + ), + accountInfoClient: .init(fetch: { userId in + + // Assert + + #expect(userId == newMemberId) + return expectedAccount + }) ) // Act @@ -63,9 +61,7 @@ struct CohabitantStoreTest { let waiterForUpdateMembers = Task { await withCheckedContinuation { continuation in - ObservationHelper.continuousObservationTracking { - store.members - } onChange: { + ObservationHelper.continuousObservationTracking({ store.members }) { continuation.resume(returning: ()) } } @@ -74,6 +70,7 @@ struct CohabitantStoreTest { continuation.yield([inputCohabitantData]) await waiterForUpdateMembers.value continuation.finish() + await store.removeSnapshotListener() #expect(store.members.value.count == 1) #expect(store.members.value.contains(.init(id: newMemberId, userName: newMemberUserName))) diff --git a/hometeTests/Domain/Housework/DailyHouseworkListTest.swift b/LocalPackage/Tests/HometeDomainTests/Housework/DailyHouseworkListTest.swift similarity index 99% rename from hometeTests/Domain/Housework/DailyHouseworkListTest.swift rename to LocalPackage/Tests/HometeDomainTests/Housework/DailyHouseworkListTest.swift index e2e9736a..654a7879 100644 --- a/hometeTests/Domain/Housework/DailyHouseworkListTest.swift +++ b/LocalPackage/Tests/HometeDomainTests/Housework/DailyHouseworkListTest.swift @@ -8,7 +8,7 @@ import Foundation import Testing -@testable import homete +@testable import HometeDomain // swiftlint:disable:next convenience_type struct DailyHouseworkListTest { diff --git a/hometeTests/Domain/Housework/HouseworkBoardListTest.swift b/LocalPackage/Tests/HometeDomainTests/Housework/HouseworkBoardListTest.swift similarity index 98% rename from hometeTests/Domain/Housework/HouseworkBoardListTest.swift rename to LocalPackage/Tests/HometeDomainTests/Housework/HouseworkBoardListTest.swift index bda8aede..cc2fb36d 100644 --- a/hometeTests/Domain/Housework/HouseworkBoardListTest.swift +++ b/LocalPackage/Tests/HometeDomainTests/Housework/HouseworkBoardListTest.swift @@ -8,7 +8,7 @@ import Foundation import Testing -@testable import homete +@testable import HometeDomain struct HouseworkBoardListTest { diff --git a/hometeTests/Domain/Housework/HouseworkHistoryListTest.swift b/LocalPackage/Tests/HometeDomainTests/Housework/HouseworkHistoryListTest.swift similarity index 98% rename from hometeTests/Domain/Housework/HouseworkHistoryListTest.swift rename to LocalPackage/Tests/HometeDomainTests/Housework/HouseworkHistoryListTest.swift index 64485e54..1c73ded5 100644 --- a/hometeTests/Domain/Housework/HouseworkHistoryListTest.swift +++ b/LocalPackage/Tests/HometeDomainTests/Housework/HouseworkHistoryListTest.swift @@ -6,7 +6,7 @@ // import Testing -@testable import homete +@testable import HometeDomain // swiftlint:disable:next convenience_type struct HouseworkHistoryListTest { diff --git a/hometeTests/Domain/Housework/HouseworkIndexedDate.swift b/LocalPackage/Tests/HometeDomainTests/Housework/HouseworkIndexedDate.swift similarity index 98% rename from hometeTests/Domain/Housework/HouseworkIndexedDate.swift rename to LocalPackage/Tests/HometeDomainTests/Housework/HouseworkIndexedDate.swift index c71084b1..7a89f321 100644 --- a/hometeTests/Domain/Housework/HouseworkIndexedDate.swift +++ b/LocalPackage/Tests/HometeDomainTests/Housework/HouseworkIndexedDate.swift @@ -7,7 +7,7 @@ import Foundation import Testing -@testable import homete +@testable import HometeDomain enum HouseworkIndexedDateTest { diff --git a/hometeTests/Domain/Housework/HouseworkItemTest.swift b/LocalPackage/Tests/HometeDomainTests/Housework/HouseworkItemTest.swift similarity index 99% rename from hometeTests/Domain/Housework/HouseworkItemTest.swift rename to LocalPackage/Tests/HometeDomainTests/Housework/HouseworkItemTest.swift index 4ec1eb20..6a446531 100644 --- a/hometeTests/Domain/Housework/HouseworkItemTest.swift +++ b/LocalPackage/Tests/HometeDomainTests/Housework/HouseworkItemTest.swift @@ -8,7 +8,7 @@ import Foundation import Testing -@testable import homete +@testable import HometeDomain enum HouseworkItemTest { diff --git a/hometeTests/Domain/Housework/HouseworkListStoreTest.swift b/LocalPackage/Tests/HometeDomainTests/Housework/HouseworkListStoreTest.swift similarity index 98% rename from hometeTests/Domain/Housework/HouseworkListStoreTest.swift rename to LocalPackage/Tests/HometeDomainTests/Housework/HouseworkListStoreTest.swift index 101fdbf0..52ae4fdb 100644 --- a/hometeTests/Domain/Housework/HouseworkListStoreTest.swift +++ b/LocalPackage/Tests/HometeDomainTests/Housework/HouseworkListStoreTest.swift @@ -8,7 +8,7 @@ import Foundation import Testing -@testable import homete +@testable import HometeDomain @MainActor struct HouseworkListStoreTest { @@ -55,9 +55,7 @@ struct HouseworkListStoreTest { let waiterForUpdateItems = Task { await withCheckedContinuation { continuation in - ObservationHelper.continuousObservationTracking { - store.items - } onChange: { + ObservationHelper.continuousObservationTracking({ store.items }) { continuation.resume(returning: ()) } } @@ -69,6 +67,7 @@ struct HouseworkListStoreTest { continuation.yield(inputHouseworkList) await waiterForUpdateItems.value continuation.finish() + await store.stopObserving() #expect( store.items == .init(value: [ diff --git a/hometeTests/Domain/Housework/StoredAllHouseworkListTest.swift b/LocalPackage/Tests/HometeDomainTests/Housework/StoredAllHouseworkListTest.swift similarity index 98% rename from hometeTests/Domain/Housework/StoredAllHouseworkListTest.swift rename to LocalPackage/Tests/HometeDomainTests/Housework/StoredAllHouseworkListTest.swift index 7c58f494..b05fc108 100644 --- a/hometeTests/Domain/Housework/StoredAllHouseworkListTest.swift +++ b/LocalPackage/Tests/HometeDomainTests/Housework/StoredAllHouseworkListTest.swift @@ -7,7 +7,7 @@ import Foundation import Testing -@testable import homete +@testable import HometeDomain struct StoredAllHouseworkListTest { diff --git a/hometeTests/TestHelper/AccountListenerStreamCreater.swift b/LocalPackage/Tests/HometeDomainTests/TestHelper/AccountListenerStreamCreater.swift similarity index 92% rename from hometeTests/TestHelper/AccountListenerStreamCreater.swift rename to LocalPackage/Tests/HometeDomainTests/TestHelper/AccountListenerStreamCreater.swift index 0f07c944..858e2ddc 100644 --- a/hometeTests/TestHelper/AccountListenerStreamCreater.swift +++ b/LocalPackage/Tests/HometeDomainTests/TestHelper/AccountListenerStreamCreater.swift @@ -6,7 +6,7 @@ // import Foundation -@testable import homete +@testable import HometeDomain extension AccountListenerStream { diff --git a/hometeTests/TestHelper/CalendarHelper.swift b/LocalPackage/Tests/HometeDomainTests/TestHelper/CalendarHelper.swift similarity index 100% rename from hometeTests/TestHelper/CalendarHelper.swift rename to LocalPackage/Tests/HometeDomainTests/TestHelper/CalendarHelper.swift diff --git a/hometeTests/TestHelper/DailyHouseworkListHelper.swift b/LocalPackage/Tests/HometeDomainTests/TestHelper/DailyHouseworkListHelper.swift similarity index 93% rename from hometeTests/TestHelper/DailyHouseworkListHelper.swift rename to LocalPackage/Tests/HometeDomainTests/TestHelper/DailyHouseworkListHelper.swift index 34a03a70..445c2ffa 100644 --- a/hometeTests/TestHelper/DailyHouseworkListHelper.swift +++ b/LocalPackage/Tests/HometeDomainTests/TestHelper/DailyHouseworkListHelper.swift @@ -5,7 +5,7 @@ // Created by 佐藤汰一 on 2025/10/02. // -@testable import homete +@testable import HometeDomain extension DailyHouseworkList { diff --git a/hometeTests/TestHelper/DateHelper.swift b/LocalPackage/Tests/HometeDomainTests/TestHelper/DateHelper.swift similarity index 100% rename from hometeTests/TestHelper/DateHelper.swift rename to LocalPackage/Tests/HometeDomainTests/TestHelper/DateHelper.swift diff --git a/hometeTests/TestHelper/HouseworkItemHelper.swift b/LocalPackage/Tests/HometeDomainTests/TestHelper/HouseworkItemHelper.swift similarity index 98% rename from hometeTests/TestHelper/HouseworkItemHelper.swift rename to LocalPackage/Tests/HometeDomainTests/TestHelper/HouseworkItemHelper.swift index 7517c4d3..3e0d3b0b 100644 --- a/hometeTests/TestHelper/HouseworkItemHelper.swift +++ b/LocalPackage/Tests/HometeDomainTests/TestHelper/HouseworkItemHelper.swift @@ -6,7 +6,7 @@ // import Foundation -@testable import homete +@testable import HometeDomain extension HouseworkItem { diff --git a/hometeTests/TestHelper/LocaleHelper.swift b/LocalPackage/Tests/HometeDomainTests/TestHelper/LocaleHelper.swift similarity index 100% rename from hometeTests/TestHelper/LocaleHelper.swift rename to LocalPackage/Tests/HometeDomainTests/TestHelper/LocaleHelper.swift diff --git a/hometeTests/TestHelper/ObservationHelper.swift b/LocalPackage/Tests/HometeDomainTests/TestHelper/ObservationHelper.swift similarity index 55% rename from hometeTests/TestHelper/ObservationHelper.swift rename to LocalPackage/Tests/HometeDomainTests/TestHelper/ObservationHelper.swift index 086e4c1a..d4059a64 100644 --- a/hometeTests/TestHelper/ObservationHelper.swift +++ b/LocalPackage/Tests/HometeDomainTests/TestHelper/ObservationHelper.swift @@ -13,15 +13,18 @@ enum ObservationHelper { /// - Parameters: /// - apply: 変更を検知したいプロパティを返す /// - onChange: 変更検知時に発火するクロージャ - static func continuousObservationTracking( - _ apply: @escaping () -> T, - onChange: @escaping (@Sendable () -> Void) + @MainActor + static func continuousObservationTracking( + _ apply: @escaping @MainActor @Sendable () -> T, + onChange: @escaping @Sendable () -> Void ) { - _ = withObservationTracking(apply) { + _ = withObservationTracking({ apply() }) { onChange() - continuousObservationTracking(apply, onChange: onChange) + Task { @MainActor in + continuousObservationTracking(apply, onChange: onChange) + } } } } diff --git a/hometeTests/TestHelper/TimeZoneHelper.swift b/LocalPackage/Tests/HometeDomainTests/TestHelper/TimeZoneHelper.swift similarity index 100% rename from hometeTests/TestHelper/TimeZoneHelper.swift rename to LocalPackage/Tests/HometeDomainTests/TestHelper/TimeZoneHelper.swift diff --git a/LocalPackage/Tests/LocalPackageTests/LocalPackageTests.swift b/LocalPackage/Tests/LocalPackageTests/LocalPackageTests.swift deleted file mode 100644 index f1376e7e..00000000 --- a/LocalPackage/Tests/LocalPackageTests/LocalPackageTests.swift +++ /dev/null @@ -1,6 +0,0 @@ -import Testing -@testable import LocalPackage - -@Test func example() async throws { - // Write your test here and use APIs like `#expect(...)` to check expected conditions. -} diff --git a/homete.xcodeproj/project.pbxproj b/homete.xcodeproj/project.pbxproj index 08c52b64..71dd7397 100644 --- a/homete.xcodeproj/project.pbxproj +++ b/homete.xcodeproj/project.pbxproj @@ -7,6 +7,7 @@ objects = { /* Begin PBXBuildFile section */ + 04B371992F40526F00820C62 /* HometeDomain in Frameworks */ = {isa = PBXBuildFile; productRef = 04B371982F40526F00820C62 /* HometeDomain */; }; BC0CF70D2E77B3A500DC31CA /* FirebaseMessaging in Frameworks */ = {isa = PBXBuildFile; productRef = BC0CF70C2E77B3A500DC31CA /* FirebaseMessaging */; }; BC0CF7122E7863B500DC31CA /* FirebaseFunctions in Frameworks */ = {isa = PBXBuildFile; productRef = BC0CF7112E7863B500DC31CA /* FirebaseFunctions */; }; BC1BB2722E909B99001D168F /* SnapshotTesting in Frameworks */ = {isa = PBXBuildFile; productRef = BC1BB2712E909B99001D168F /* SnapshotTesting */; }; @@ -29,13 +30,6 @@ remoteGlobalIDString = BCC853F42DB74BBC00C9A44B; remoteInfo = homete; }; - BCC854032DB74BBF00C9A44B /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = BCC853ED2DB74BBC00C9A44B /* Project object */; - proxyType = 1; - remoteGlobalIDString = BCC853F42DB74BBC00C9A44B; - remoteInfo = homete; - }; /* End PBXContainerItemProxy section */ /* Begin PBXFileReference section */ @@ -46,7 +40,6 @@ BC5204662DB7BEFD0097472D /* homete.xctestplan */ = {isa = PBXFileReference; lastKnownFileType = text; path = homete.xctestplan; sourceTree = ""; }; BC7469462DBCBE71007E4222 /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; lastKnownFileType = text.xml; path = PrivacyInfo.xcprivacy; sourceTree = ""; }; BCC853F52DB74BBC00C9A44B /* homete.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = homete.app; sourceTree = BUILT_PRODUCTS_DIR; }; - BCC854022DB74BBF00C9A44B /* hometeTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = hometeTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; /* End PBXFileReference section */ /* Begin PBXFileSystemSynchronizedBuildFileExceptionSet section */ @@ -73,11 +66,6 @@ path = homete; sourceTree = ""; }; - BCC854052DB74BBF00C9A44B /* hometeTests */ = { - isa = PBXFileSystemSynchronizedRootGroup; - path = hometeTests; - sourceTree = ""; - }; /* End PBXFileSystemSynchronizedRootGroup section */ /* Begin PBXFrameworksBuildPhase section */ @@ -100,17 +88,11 @@ BC0CF70D2E77B3A500DC31CA /* FirebaseMessaging in Frameworks */, BC0CF7122E7863B500DC31CA /* FirebaseFunctions in Frameworks */, BC2E372C2E3DE189005BD910 /* FirebaseAuth in Frameworks */, + 04B371992F40526F00820C62 /* HometeDomain in Frameworks */, BCB8DB182E37B3F900FFB4E1 /* FirebaseCrashlytics in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; - BCC853FF2DB74BBF00C9A44B /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ @@ -130,7 +112,6 @@ BC7469462DBCBE71007E4222 /* PrivacyInfo.xcprivacy */, BC5204662DB7BEFD0097472D /* homete.xctestplan */, BCC853F72DB74BBC00C9A44B /* homete */, - BCC854052DB74BBF00C9A44B /* hometeTests */, BC1BB2692E909B8C001D168F /* hometeSnapshotTests */, BCB8DB162E37B3F900FFB4E1 /* Frameworks */, BCC853F62DB74BBC00C9A44B /* Products */, @@ -141,7 +122,6 @@ isa = PBXGroup; children = ( BCC853F52DB74BBC00C9A44B /* homete.app */, - BCC854022DB74BBF00C9A44B /* hometeTests.xctest */, BC1BB2682E909B8C001D168F /* hometeSnapshotTests.xctest */, ); name = Products; @@ -202,34 +182,12 @@ BC0CF70C2E77B3A500DC31CA /* FirebaseMessaging */, BC0CF7112E7863B500DC31CA /* FirebaseFunctions */, BCADFE1C2EACB62E00DF4AAA /* Prefire */, + 04B371982F40526F00820C62 /* HometeDomain */, ); productName = homete; productReference = BCC853F52DB74BBC00C9A44B /* homete.app */; productType = "com.apple.product-type.application"; }; - BCC854012DB74BBF00C9A44B /* hometeTests */ = { - isa = PBXNativeTarget; - buildConfigurationList = BCC854192DB74BBF00C9A44B /* Build configuration list for PBXNativeTarget "hometeTests" */; - buildPhases = ( - BCC853FE2DB74BBF00C9A44B /* Sources */, - BCC853FF2DB74BBF00C9A44B /* Frameworks */, - BCC854002DB74BBF00C9A44B /* Resources */, - ); - buildRules = ( - ); - dependencies = ( - BCC854042DB74BBF00C9A44B /* PBXTargetDependency */, - ); - fileSystemSynchronizedGroups = ( - BCC854052DB74BBF00C9A44B /* hometeTests */, - ); - name = hometeTests; - packageProductDependencies = ( - ); - productName = hometeTests; - productReference = BCC854022DB74BBF00C9A44B /* hometeTests.xctest */; - productType = "com.apple.product-type.bundle.unit-test"; - }; /* End PBXNativeTarget section */ /* Begin PBXProject section */ @@ -247,11 +205,6 @@ BCC853F42DB74BBC00C9A44B = { CreatedOnToolsVersion = 16.3; }; - BCC854012DB74BBF00C9A44B = { - CreatedOnToolsVersion = 16.3; - LastSwiftMigration = 1640; - TestTargetID = BCC853F42DB74BBC00C9A44B; - }; }; }; buildConfigurationList = BCC853F02DB74BBC00C9A44B /* Build configuration list for PBXProject "homete" */; @@ -268,6 +221,7 @@ BCB8DAFC2E36FC7100FFB4E1 /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */, BC1BB2612E909B50001D168F /* XCRemoteSwiftPackageReference "swift-snapshot-testing" */, BC4701F92E9A0F3D006CC530 /* XCRemoteSwiftPackageReference "Prefire" */, + 04B371972F40526F00820C62 /* XCLocalSwiftPackageReference "LocalPackage" */, ); preferredProjectObjectVersion = 77; productRefGroup = BCC853F62DB74BBC00C9A44B /* Products */; @@ -275,7 +229,6 @@ projectRoot = ""; targets = ( BCC853F42DB74BBC00C9A44B /* homete */, - BCC854012DB74BBF00C9A44B /* hometeTests */, BC1BB2672E909B8C001D168F /* hometeSnapshotTests */, ); }; @@ -299,13 +252,6 @@ ); runOnlyForDeploymentPostprocessing = 0; }; - BCC854002DB74BBF00C9A44B /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ @@ -369,13 +315,6 @@ ); runOnlyForDeploymentPostprocessing = 0; }; - BCC853FE2DB74BBF00C9A44B /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; /* End PBXSourcesBuildPhase section */ /* Begin PBXTargetDependency section */ @@ -388,11 +327,6 @@ isa = PBXTargetDependency; productRef = BC4701FC2E9A0F49006CC530 /* PrefireTestsPlugin */; }; - BCC854042DB74BBF00C9A44B /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = BCC853F42DB74BBC00C9A44B /* homete */; - targetProxy = BCC854032DB74BBF00C9A44B /* PBXContainerItemProxy */; - }; /* End PBXTargetDependency section */ /* Begin XCBuildConfiguration section */ @@ -668,57 +602,6 @@ }; name = Release; }; - BCC8541A2DB74BBF00C9A44B /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - BUNDLE_LOADER = "$(TEST_HOST)"; - CLANG_ENABLE_MODULES = YES; - CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; - DEVELOPMENT_TEAM = 56LYVN6DMF; - ENABLE_USER_SCRIPT_SANDBOXING = NO; - GENERATE_INFOPLIST_FILE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 17.0; - MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = taichi.satou.hometeTests; - PRODUCT_NAME = "$(TARGET_NAME)"; - SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; - SUPPORTS_MACCATALYST = NO; - SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; - SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; - SWIFT_EMIT_LOC_STRINGS = NO; - SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; - TEST_HOST = "$(BUILT_PRODUCTS_DIR)/homete.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/homete"; - }; - name = Debug; - }; - BCC8541B2DB74BBF00C9A44B /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - BUNDLE_LOADER = "$(TEST_HOST)"; - CLANG_ENABLE_MODULES = YES; - CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; - DEVELOPMENT_TEAM = 56LYVN6DMF; - ENABLE_USER_SCRIPT_SANDBOXING = NO; - GENERATE_INFOPLIST_FILE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 17.0; - MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = taichi.satou.hometeTests; - PRODUCT_NAME = "$(TARGET_NAME)"; - SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; - SUPPORTS_MACCATALYST = NO; - SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; - SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; - SWIFT_EMIT_LOC_STRINGS = NO; - SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; - TEST_HOST = "$(BUILT_PRODUCTS_DIR)/homete.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/homete"; - }; - name = Release; - }; /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ @@ -749,17 +632,15 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; - BCC854192DB74BBF00C9A44B /* Build configuration list for PBXNativeTarget "hometeTests" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - BCC8541A2DB74BBF00C9A44B /* Debug */, - BCC8541B2DB74BBF00C9A44B /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; /* End XCConfigurationList section */ +/* Begin XCLocalSwiftPackageReference section */ + 04B371972F40526F00820C62 /* XCLocalSwiftPackageReference "LocalPackage" */ = { + isa = XCLocalSwiftPackageReference; + relativePath = LocalPackage; + }; +/* End XCLocalSwiftPackageReference section */ + /* Begin XCRemoteSwiftPackageReference section */ BC1BB2612E909B50001D168F /* XCRemoteSwiftPackageReference "swift-snapshot-testing" */ = { isa = XCRemoteSwiftPackageReference; @@ -788,6 +669,10 @@ /* End XCRemoteSwiftPackageReference section */ /* Begin XCSwiftPackageProductDependency section */ + 04B371982F40526F00820C62 /* HometeDomain */ = { + isa = XCSwiftPackageProductDependency; + productName = HometeDomain; + }; BC0CF70C2E77B3A500DC31CA /* FirebaseMessaging */ = { isa = XCSwiftPackageProductDependency; package = BCB8DAFC2E36FC7100FFB4E1 /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */; diff --git a/homete.xctestplan b/homete.xctestplan index d9dcc27e..0a4ab649 100644 --- a/homete.xctestplan +++ b/homete.xctestplan @@ -31,11 +31,10 @@ }, "testTargets" : [ { - "parallelizable" : true, "target" : { - "containerPath" : "container:homete.xcodeproj", - "identifier" : "BCC854012DB74BBF00C9A44B", - "name" : "hometeTests" + "containerPath" : "container:LocalPackage", + "identifier" : "HometeDomainTests", + "name" : "HometeDomainTests" } } ], diff --git a/homete/Model/AppDependencies.swift b/homete/Model/AppDependencies.swift index dc46e4de..f28d7a8d 100644 --- a/homete/Model/AppDependencies.swift +++ b/homete/Model/AppDependencies.swift @@ -5,6 +5,7 @@ // Created by 佐藤汰一 on 2025/08/03. // +import HometeDomain import SwiftUI struct AppDependencies { diff --git a/homete/Model/Dependencies/AnalyticsClient.swift b/homete/Model/Dependencies/AnalyticsClient.swift deleted file mode 100644 index c7ff90d3..00000000 --- a/homete/Model/Dependencies/AnalyticsClient.swift +++ /dev/null @@ -1,38 +0,0 @@ -// -// AnalyticsClient.swift -// homete -// -// Created by 佐藤汰一 on 2025/08/09. -// - -import FirebaseAnalytics -import FirebaseCrashlytics - -struct AnalyticsClient { - - let setId: @Sendable (String) -> Void - let log: @Sendable (AnalyticsEvent) -> Void - - init( - setId: @Sendable @escaping (String) -> Void = { _ in }, - log: @Sendable @escaping (AnalyticsEvent) -> Void = { _ in } - ) { - - self.setId = setId - self.log = log - } -} - -extension AnalyticsClient: DependencyClient { - - static let liveValue: AnalyticsClient = .init { userId in - - Analytics.setUserID(userId) - Crashlytics.crashlytics().setUserID(userId) - } log: { event in - - Analytics.logEvent(event.name, parameters: event.parameters) - } - - static let previewValue = AnalyticsClient() -} diff --git a/homete/Model/Dependencies/CohabitantClient.swift b/homete/Model/Dependencies/CohabitantClient.swift deleted file mode 100644 index b467c297..00000000 --- a/homete/Model/Dependencies/CohabitantClient.swift +++ /dev/null @@ -1,54 +0,0 @@ -// -// CohabitantClient.swift -// homete -// -// Created by 佐藤汰一 on 2025/08/18. -// - -import FirebaseFirestore - -struct CohabitantClient { - - let register: @Sendable (CohabitantData) async throws -> Void - let addSnapshotListener: @Sendable ( - _ listenerId: String, - _ cohabitantId: String - ) async -> AsyncStream<[CohabitantData]> - let removeSnapshotListener: @Sendable (_ listenerId: String) async -> Void - - init( - register: @Sendable @escaping (CohabitantData) async throws -> Void = { _ in }, - addSnapshotListener: @Sendable @escaping ( - _: String, - _: String - ) async -> AsyncStream<[CohabitantData]> = { _, _ in .init { nil } }, - removeSnapshotListener: @Sendable @escaping (_ listenerId: String) async -> Void = { _ in } - ) { - - self.register = register - self.addSnapshotListener = addSnapshotListener - self.removeSnapshotListener = removeSnapshotListener - } -} - -extension CohabitantClient: DependencyClient { - - static let liveValue: CohabitantClient = .init { data in - - try await FirestoreService.shared.insertOrUpdate(data: data) { - - return $0.cohabitantRef(id: data.id) - } - } addSnapshotListener: { listenerId, cohabitantId in - - return await FirestoreService.shared.addSnapshotListener(id: listenerId) { - $0.collection(path: .cohabitant) - .whereField(CohabitantData.idField, isEqualTo: cohabitantId) - } - } removeSnapshotListener: { listenerId in - - await FirestoreService.shared.removeSnapshotListener(id: listenerId) - } - - static let previewValue: CohabitantClient = .init() -} diff --git a/homete/Model/Dependencies/HouseworkClient.swift b/homete/Model/Dependencies/HouseworkClient.swift deleted file mode 100644 index af3f77d1..00000000 --- a/homete/Model/Dependencies/HouseworkClient.swift +++ /dev/null @@ -1,84 +0,0 @@ -// -// HouseworkClient.swift -// homete -// -// Created by 佐藤汰一 on 2025/09/07. -// - -import FirebaseFirestore - -struct HouseworkClient { - - let insertOrUpdateItem: @Sendable (_ item: HouseworkItem, _ cohabitantId: String) async throws -> Void - let removeItem: @Sendable (_ item: HouseworkItem, _ cohabitantId: String) async throws -> Void - let snapshotListener: @Sendable ( - _ id: String, - _ cohabitantId: String, - _ anchorDate: Date, - _ offset: Int - ) async -> AsyncStream<[HouseworkItem]> - let removeListener: @Sendable (_ id: String) async -> Void -} - -extension HouseworkClient: DependencyClient { - - init( - insertOrUpdateItemHandler: @escaping @Sendable ( - _ item: HouseworkItem, - _ cohabitantId: String - ) async throws -> Void = { _, _ in }, - removeItemHandler: @escaping @Sendable ( - _ item: HouseworkItem, - _ cohabitantId: String - ) async throws -> Void = { _, _ in }, - snapshotListenerHandler: @escaping @Sendable ( - _ id: String, - _ cohabitantId: String, - _ anchorDate: Date, - _ offset: Int - ) async -> AsyncStream<[HouseworkItem]> = { _, _, _, _ in .makeStream().stream }, - removeListenerHandler: @escaping @Sendable (_ id: String) async -> Void = { _ in } - ) { - - insertOrUpdateItem = insertOrUpdateItemHandler - removeItem = removeItemHandler - snapshotListener = snapshotListenerHandler - removeListener = removeListenerHandler - } - - static let liveValue = HouseworkClient { item, cohabitantId in - - try await FirestoreService.shared.insertOrUpdate(data: item) { - - return $0 - .houseworkListRef(id: cohabitantId) - .document(item.id) - } - } removeItem: { item, cohabitantId in - - try await FirestoreService.shared.delete { - - return $0 - .houseworkListRef(id: cohabitantId) - .document(item.id) - } - } snapshotListener: { id, cohabitantId, anchorDate, offset in - - let targetDateList = HouseworkIndexedDate.calcTargetPeriod( - anchorDate: anchorDate, - offsetDays: offset, - calendar: .autoupdatingCurrent - ) - - return await FirestoreService.shared.addSnapshotListener(id: id) { - - return $0.houseworkListRef(id: cohabitantId) - .whereField("indexedDate", in: targetDateList) - } - } removeListener: { id in - - await FirestoreService.shared.removeSnapshotListener(id: id) - } - - static let previewValue = HouseworkClient() -} diff --git a/homete/Model/Dependencies/SignInWithAppleClient.swift b/homete/Model/Dependencies/SignInWithAppleClient.swift deleted file mode 100644 index 08ade17f..00000000 --- a/homete/Model/Dependencies/SignInWithAppleClient.swift +++ /dev/null @@ -1,32 +0,0 @@ -// -// SignInWithAppleClient.swift -// homete -// -// Created by 佐藤汰一 on 2025/12/31. -// - -import AuthenticationServices - -struct SignInWithAppleClient { - let reauthentication: @MainActor (_ nonce: SignInWithAppleNonce) async throws -> SignInWithAppleResult - - init( - reauthentication: @MainActor @escaping (_ nonce: SignInWithAppleNonce) - async throws -> SignInWithAppleResult = { _ in preconditionFailure() } - ) { - - self.reauthentication = reauthentication - } -} - -extension SignInWithAppleClient: DependencyClient { - - static let liveValue = SignInWithAppleClient { nonce in - - let signInWithApple = SignInWithApple() - let appleIDCredential = try await signInWithApple(nonce) - return try SignInWithAppleResultFactory.make(appleIDCredential, nonce) - } - - static let previewValue = SignInWithAppleClient() -} diff --git a/homete/Model/Domain/Account/Account.swift b/homete/Model/Domain/Account/Account.swift deleted file mode 100644 index d5032127..00000000 --- a/homete/Model/Domain/Account/Account.swift +++ /dev/null @@ -1,22 +0,0 @@ -// -// Account.swift -// homete -// -// Created by 佐藤汰一 on 2025/08/03. -// - -struct Account: Equatable, Codable { - - let id: String - let userName: String - let fcmToken: String? - let cohabitantId: String? -} - -extension Account { - - static func initial(auth: AccountAuthResult, userName: UserName, fcmToken: String?) -> Self { - - return .init(id: auth.id, userName: userName.value, fcmToken: fcmToken, cohabitantId: nil) - } -} diff --git a/homete/Model/Domain/Authentification/AccountAuthInfo.swift b/homete/Model/Domain/Authentification/AccountAuthInfo.swift deleted file mode 100644 index 5e52d3ac..00000000 --- a/homete/Model/Domain/Authentification/AccountAuthInfo.swift +++ /dev/null @@ -1,13 +0,0 @@ -// -// AccountAuthInfo.swift -// homete -// -// Created by 佐藤汰一 on 2025/12/31. -// - -struct AccountAuthInfo: Equatable { - let result: AccountAuthResult? - let alreadyLoadedAtInitiate: Bool - - static let initial = AccountAuthInfo(result: nil, alreadyLoadedAtInitiate: false) -} diff --git a/homete/Model/Domain/Authentification/AccountAuthResult.swift b/homete/Model/Domain/Authentification/AccountAuthResult.swift deleted file mode 100644 index 693873ec..00000000 --- a/homete/Model/Domain/Authentification/AccountAuthResult.swift +++ /dev/null @@ -1,11 +0,0 @@ -// -// AccountAuthResult.swift -// homete -// -// Created by 佐藤汰一 on 2025/08/09. -// - -struct AccountAuthResult: Equatable { - - let id: String -} diff --git a/homete/Model/Domain/Authentification/SignInWithAppleNonce.swift b/homete/Model/Domain/Authentification/SignInWithAppleNonce.swift deleted file mode 100644 index a605680d..00000000 --- a/homete/Model/Domain/Authentification/SignInWithAppleNonce.swift +++ /dev/null @@ -1,12 +0,0 @@ -// -// SignInWithAppleNonce.swift -// homete -// -// Created by 佐藤汰一 on 2025/08/04. -// - -struct SignInWithAppleNonce: Equatable { - - let original: String - let sha256: String -} diff --git a/homete/Model/Domain/Authentification/SignInWithAppleResult.swift b/homete/Model/Domain/Authentification/SignInWithAppleResult.swift deleted file mode 100644 index f26ffc2e..00000000 --- a/homete/Model/Domain/Authentification/SignInWithAppleResult.swift +++ /dev/null @@ -1,13 +0,0 @@ -// -// SignInWithAppleResult.swift -// homete -// -// Created by 佐藤汰一 on 2025/08/09. -// - -struct SignInWithAppleResult: Equatable { - - let tokenId: String - let nonce: String - let authorizationCode: String -} diff --git a/homete/Model/Domain/Cohabitant/CohabitantData.swift b/homete/Model/Domain/Cohabitant/CohabitantData.swift deleted file mode 100644 index f929ef27..00000000 --- a/homete/Model/Domain/Cohabitant/CohabitantData.swift +++ /dev/null @@ -1,16 +0,0 @@ -// -// CohabitantData.swift -// homete -// -// Created by 佐藤汰一 on 2026/01/04. -// - -struct CohabitantData: Codable { - - static let idField = "id" - - /// 家族グループのID - let id: String - /// 参加しているメンバーのユーザーID - let members: [String] -} diff --git a/homete/Model/Domain/Cohabitant/CohabitantMember.swift b/homete/Model/Domain/Cohabitant/CohabitantMember.swift deleted file mode 100644 index a7e7107d..00000000 --- a/homete/Model/Domain/Cohabitant/CohabitantMember.swift +++ /dev/null @@ -1,14 +0,0 @@ -// -// CohabitantMember.swift -// homete -// -// Created by 佐藤汰一 on 2026/01/04. -// - -struct CohabitantMember: Equatable, Hashable { - - /// メンバーのユーザーID - let id: String - /// メンバーのユーザー名 - let userName: String -} diff --git a/homete/Model/Dependencies/AccountAuthClient.swift b/homete/Model/ImplDependencies/ImplAccountAuthClient.swift similarity index 57% rename from homete/Model/Dependencies/AccountAuthClient.swift rename to homete/Model/ImplDependencies/ImplAccountAuthClient.swift index 57ee5646..00c1035e 100644 --- a/homete/Model/Dependencies/AccountAuthClient.swift +++ b/homete/Model/ImplDependencies/ImplAccountAuthClient.swift @@ -5,42 +5,10 @@ // Created by 佐藤汰一 on 2025/08/03. // +import HometeDomain import FirebaseAuth -struct AccountAuthClient { - - let signIn: @Sendable (String, String) async throws -> AccountAuthResult - let signOut: @Sendable () throws -> Void - let makeListener: @Sendable () -> AccountListenerStream - let reauthenticateWithApple: @Sendable (_ signInWithAppleResult: SignInWithAppleResult) async throws -> Void - let revokeAppleToken: @Sendable (_ authorizationCode: String) async throws -> Void - let deleteAccount: @Sendable () async throws -> Void - - init( - signIn: @Sendable @escaping (String, String) async throws -> AccountAuthResult = { _, _ in .init(id: "id") }, - signOut: @Sendable @escaping () throws -> Void = {}, - makeListener: @Sendable @escaping () -> AccountListenerStream = { - - let (stream, continuation) = AsyncStream.makeStream() - return AccountListenerStream(values: stream, - listenerToken: NSObject(), - continuation: continuation) - }, - reauthenticateWithApple: @Sendable @escaping (_: SignInWithAppleResult) async throws -> Void = { _ in }, - revokeAppleToken: @Sendable @escaping (_: String) async throws -> Void = { _ in }, - deleteAccount: @Sendable @escaping () async throws -> Void = {} - ) { - - self.signIn = signIn - self.signOut = signOut - self.makeListener = makeListener - self.reauthenticateWithApple = reauthenticateWithApple - self.revokeAppleToken = revokeAppleToken - self.deleteAccount = deleteAccount - } -} - -extension AccountAuthClient: DependencyClient { +extension AccountAuthClient { static let liveValue: AccountAuthClient = .init( signIn: { tokenId, nonce in @@ -80,8 +48,6 @@ extension AccountAuthClient: DependencyClient { try await user.delete() } ) - - static let previewValue = AccountAuthClient() } private extension AccountAuthClient { diff --git a/homete/Model/Dependencies/AccountInfoClient.swift b/homete/Model/ImplDependencies/ImplAccountInfoClient.swift similarity index 52% rename from homete/Model/Dependencies/AccountInfoClient.swift rename to homete/Model/ImplDependencies/ImplAccountInfoClient.swift index 89e71b89..9d74b283 100644 --- a/homete/Model/Dependencies/AccountInfoClient.swift +++ b/homete/Model/ImplDependencies/ImplAccountInfoClient.swift @@ -6,22 +6,9 @@ // import FirebaseFirestore +import HometeDomain -struct AccountInfoClient { - - let insertOrUpdate: @Sendable (Account) async throws -> Void - let fetch: @Sendable (String) async throws -> Account? - - init( - insertOrUpdate: @Sendable @escaping (Account) async throws -> Void = { _ in }, - fetch: @Sendable @escaping (String) async throws -> Account? = { _ in nil } - ) { - self.insertOrUpdate = insertOrUpdate - self.fetch = fetch - } -} - -extension AccountInfoClient: DependencyClient { +extension AccountInfoClient { private static let collectionPath = "Account" private static let primaryKey = "id" @@ -39,6 +26,4 @@ extension AccountInfoClient: DependencyClient { return $0.accountRef(id: id) } } - - static let previewValue: AccountInfoClient = .init() } diff --git a/homete/Model/ImplDependencies/ImplAnalyticsClient.swift b/homete/Model/ImplDependencies/ImplAnalyticsClient.swift new file mode 100644 index 00000000..9f1af32d --- /dev/null +++ b/homete/Model/ImplDependencies/ImplAnalyticsClient.swift @@ -0,0 +1,22 @@ +// +// AnalyticsClient.swift +// homete +// +// Created by 佐藤汰一 on 2025/08/09. +// + +import FirebaseAnalytics +import FirebaseCrashlytics +import HometeDomain + +extension AnalyticsClient { + + static let liveValue: AnalyticsClient = .init { userId in + + Analytics.setUserID(userId) + Crashlytics.crashlytics().setUserID(userId) + } log: { event in + + Analytics.logEvent(event.name, parameters: event.parameters) + } +} diff --git a/homete/Model/ImplDependencies/ImplCohabitantClient.swift b/homete/Model/ImplDependencies/ImplCohabitantClient.swift new file mode 100644 index 00000000..34c07770 --- /dev/null +++ b/homete/Model/ImplDependencies/ImplCohabitantClient.swift @@ -0,0 +1,29 @@ +// +// CohabitantClient.swift +// homete +// +// Created by 佐藤汰一 on 2025/08/18. +// + +import FirebaseFirestore +import HometeDomain + +extension CohabitantClient { + + static let liveValue: CohabitantClient = .init { data in + + try await FirestoreService.shared.insertOrUpdate(data: data) { + + return $0.cohabitantRef(id: data.id) + } + } addSnapshotListener: { listenerId, cohabitantId in + + return await FirestoreService.shared.addSnapshotListener(id: listenerId) { + $0.collection(path: .cohabitant) + .whereField(CohabitantData.idField, isEqualTo: cohabitantId) + } + } removeSnapshotListener: { listenerId in + + await FirestoreService.shared.removeSnapshotListener(id: listenerId) + } +} diff --git a/homete/Model/Dependencies/CohabitantPushNotificationClient.swift b/homete/Model/ImplDependencies/ImplCohabitantPushNotificationClient.swift similarity index 62% rename from homete/Model/Dependencies/CohabitantPushNotificationClient.swift rename to homete/Model/ImplDependencies/ImplCohabitantPushNotificationClient.swift index 0c536610..bb6a650e 100644 --- a/homete/Model/Dependencies/CohabitantPushNotificationClient.swift +++ b/homete/Model/ImplDependencies/ImplCohabitantPushNotificationClient.swift @@ -6,12 +6,9 @@ // import FirebaseFunctions +import HometeDomain -struct CohabitantPushNotificationClient { - let send: @Sendable (_ id: String, _ content: PushNotificationContent) async throws -> Void -} - -extension CohabitantPushNotificationClient: DependencyClient { +extension CohabitantPushNotificationClient { static let liveValue: CohabitantPushNotificationClient = .init { id, content in _ = try await Functions.functions() @@ -22,6 +19,4 @@ extension CohabitantPushNotificationClient: DependencyClient { "body": content.message ]) } - - static let previewValue: CohabitantPushNotificationClient = .init { _, _ in } } diff --git a/homete/Model/ImplDependencies/ImplHouseworkClient.swift b/homete/Model/ImplDependencies/ImplHouseworkClient.swift new file mode 100644 index 00000000..40a0e590 --- /dev/null +++ b/homete/Model/ImplDependencies/ImplHouseworkClient.swift @@ -0,0 +1,46 @@ +// +// HouseworkClient.swift +// homete +// +// Created by 佐藤汰一 on 2025/09/07. +// + +import FirebaseFirestore +import HometeDomain + +extension HouseworkClient { + + static let liveValue = HometeDomain.HouseworkClient { item, cohabitantId in + + try await FirestoreService.shared.insertOrUpdate(data: item) { + + return $0 + .houseworkListRef(id: cohabitantId) + .document(item.id) + } + } removeItemHandler: { item, cohabitantId in + + try await FirestoreService.shared.delete { + + return $0 + .houseworkListRef(id: cohabitantId) + .document(item.id) + } + } snapshotListenerHandler: { id, cohabitantId, anchorDate, offset in + + let targetDateList = HouseworkIndexedDate.calcTargetPeriod( + anchorDate: anchorDate, + offsetDays: offset, + calendar: .autoupdatingCurrent + ) + + return await FirestoreService.shared.addSnapshotListener(id: id) { + + return $0.houseworkListRef(id: cohabitantId) + .whereField("indexedDate", in: targetDateList) + } + } removeListenerHandler: { id in + + await FirestoreService.shared.removeSnapshotListener(id: id) + } +} diff --git a/homete/Model/Dependencies/NonceGenerationClient.swift b/homete/Model/ImplDependencies/ImplNonceGenerationClient.swift similarity index 76% rename from homete/Model/Dependencies/NonceGenerationClient.swift rename to homete/Model/ImplDependencies/ImplNonceGenerationClient.swift index c99bf688..c49fb7cd 100644 --- a/homete/Model/Dependencies/NonceGenerationClient.swift +++ b/homete/Model/ImplDependencies/ImplNonceGenerationClient.swift @@ -7,17 +7,9 @@ import CryptoKit import Foundation +import HometeDomain -struct NonceGenerationClient { - - let value: @Sendable () -> SignInWithAppleNonce - func callAsFunction() -> SignInWithAppleNonce { - - return value() - } -} - -extension NonceGenerationClient: DependencyClient { +extension NonceGenerationClient { static let liveValue: NonceGenerationClient = .init { @@ -49,9 +41,4 @@ extension NonceGenerationClient: DependencyClient { let hashString = hashedData.compactMap { String(format: "%02x", $0) }.joined() return hashString } - - static let previewValue: NonceGenerationClient = .init { - - return .init(original: "preview", sha256: "preview sha256") - } } diff --git a/homete/Model/ImplDependencies/ImplSignInWithAppleClient.swift b/homete/Model/ImplDependencies/ImplSignInWithAppleClient.swift new file mode 100644 index 00000000..7bf66c38 --- /dev/null +++ b/homete/Model/ImplDependencies/ImplSignInWithAppleClient.swift @@ -0,0 +1,19 @@ +// +// SignInWithAppleClient.swift +// homete +// +// Created by 佐藤汰一 on 2025/12/31. +// + +import AuthenticationServices +import HometeDomain + +extension SignInWithAppleClient { + + static let liveValue = SignInWithAppleClient { nonce in + + let signInWithApple = SignInWithApple() + let appleIDCredential = try await signInWithApple(nonce) + return try SignInWithAppleResultFactory.make(appleIDCredential, nonce) + } +} diff --git a/homete/Model/Domain/Cohabitant/CohabitantRegistration/ConfirmedRegistrationPeers.swift b/homete/Model/Service/P2P/ConfirmedRegistrationPeers.swift similarity index 79% rename from homete/Model/Domain/Cohabitant/CohabitantRegistration/ConfirmedRegistrationPeers.swift rename to homete/Model/Service/P2P/ConfirmedRegistrationPeers.swift index fbec8f12..013835ff 100644 --- a/homete/Model/Domain/Cohabitant/CohabitantRegistration/ConfirmedRegistrationPeers.swift +++ b/homete/Model/Service/P2P/ConfirmedRegistrationPeers.swift @@ -1,28 +1,21 @@ -// -// ConfirmedRegistrationPeers.swift -// homete -// -// Created by 佐藤汰一 on 2025/08/30. -// - import MultipeerConnectivity struct ConfirmedRegistrationPeers: Equatable { - + private var peers: Set - + init(peers: Set) { - + self.peers = peers } - + mutating func addPeer(_ peer: MCPeerID) { - + peers.insert(peer) } - + func isLeadPeer(connectedPeers: Set, myPeerID: MCPeerID) -> Bool? { - + guard peers == connectedPeers else { return nil } let firstPeerID = ([myPeerID] + connectedPeers) .sorted { $0.displayName < $1.displayName }.first diff --git a/homete/Model/Service/SignInWithApple/SignInWithApple.swift b/homete/Model/Service/SignInWithApple/SignInWithApple.swift index 537bb297..13f9c9b2 100644 --- a/homete/Model/Service/SignInWithApple/SignInWithApple.swift +++ b/homete/Model/Service/SignInWithApple/SignInWithApple.swift @@ -1,4 +1,5 @@ import AuthenticationServices +import HometeDomain final class SignInWithApple: NSObject, ASAuthorizationControllerDelegate { diff --git a/homete/Model/Service/SignInWithApple/SignInWithAppleRequestFactory.swift b/homete/Model/Service/SignInWithApple/SignInWithAppleRequestFactory.swift index 12bb1e4e..b51362a0 100644 --- a/homete/Model/Service/SignInWithApple/SignInWithAppleRequestFactory.swift +++ b/homete/Model/Service/SignInWithApple/SignInWithAppleRequestFactory.swift @@ -6,6 +6,7 @@ // import AuthenticationServices +import HometeDomain enum SignInWithAppleRequestFactory { diff --git a/homete/Model/Service/SignInWithApple/SignInWithAppleResultFactory.swift b/homete/Model/Service/SignInWithApple/SignInWithAppleResultFactory.swift index 834c8ac2..07450f2b 100644 --- a/homete/Model/Service/SignInWithApple/SignInWithAppleResultFactory.swift +++ b/homete/Model/Service/SignInWithApple/SignInWithAppleResultFactory.swift @@ -6,6 +6,7 @@ // import AuthenticationServices +import HometeDomain enum SignInWithAppleResultFactory { static func make( diff --git a/homete/Resouces/Localizable.xcstrings b/homete/Resouces/Localizable.xcstrings index 864de677..5b5903c4 100644 --- a/homete/Resouces/Localizable.xcstrings +++ b/homete/Resouces/Localizable.xcstrings @@ -95,9 +95,6 @@ }, "はじめまして!" : { - }, - "プライバシーポリシー" : { - }, "ポイント" : { @@ -129,9 +126,6 @@ } } } - }, - "ライセンス" : { - }, "ログアウト" : { @@ -196,9 +190,6 @@ }, "家事がありません" : { - }, - "家事テンプレート" : { - }, "家事のテンプレートが設定されていません" : { diff --git a/homete/Views/Auth/Login/LoginView.swift b/homete/Views/Auth/Login/LoginView.swift index f50c7a32..71b0d023 100644 --- a/homete/Views/Auth/Login/LoginView.swift +++ b/homete/Views/Auth/Login/LoginView.swift @@ -5,6 +5,7 @@ // Created by 佐藤汰一 on 2025/08/03. // +import HometeDomain import SwiftUI struct LoginView: View { @@ -65,5 +66,5 @@ private extension LoginView { #Preview { LoginView() - .environment(AccountAuthStore(appDependencies: .previewValue)) + .environment(AccountAuthStore()) } diff --git a/homete/Views/Auth/Login/SignInUpWithAppleButton.swift b/homete/Views/Auth/Login/SignInUpWithAppleButton.swift index 18ec72dc..0eee7a88 100644 --- a/homete/Views/Auth/Login/SignInUpWithAppleButton.swift +++ b/homete/Views/Auth/Login/SignInUpWithAppleButton.swift @@ -7,6 +7,7 @@ import AuthenticationServices import SwiftUI +import HometeDomain struct SignInUpWithAppleButton: View { @Environment(\.colorScheme) var colorScheme diff --git a/homete/Views/Auth/RegistrationAccount/RegistrationAccountView.swift b/homete/Views/Auth/RegistrationAccount/RegistrationAccountView.swift index af22a728..e64fcb7c 100644 --- a/homete/Views/Auth/RegistrationAccount/RegistrationAccountView.swift +++ b/homete/Views/Auth/RegistrationAccount/RegistrationAccountView.swift @@ -6,6 +6,7 @@ // import SwiftUI +import HometeDomain struct RegistrationAccountView: View { @Environment(AccountStore.self) var accountStore @@ -86,14 +87,14 @@ private extension RegistrationAccountView { } #Preview("RegistrationAccountView_未入力") { - RegistrationAccountView(authInfo: .init(id: "")) - .environment(AccountStore(appDependencies: .previewValue)) + RegistrationAccountView(authInfo: AccountAuthResult(id: "")) + .environment(AccountStore()) } #Preview("RegistrationAccountView_入力済み") { RegistrationAccountView( loadingState: .init(store: .init(isLoading: true)), - authInfo: .init(id: "Test") + authInfo: AccountAuthResult(id: "Test") ) - .environment(AccountStore(appDependencies: .previewValue)) + .environment(AccountStore()) } diff --git a/homete/Views/Auth/RegistrationAccount/UserNameInputTextField.swift b/homete/Views/Auth/RegistrationAccount/UserNameInputTextField.swift index 06c46ea2..9813c9b3 100644 --- a/homete/Views/Auth/RegistrationAccount/UserNameInputTextField.swift +++ b/homete/Views/Auth/RegistrationAccount/UserNameInputTextField.swift @@ -5,6 +5,7 @@ // Created by 佐藤汰一 on 2025/12/27. // +import HometeDomain import SwiftUI struct UserNameInputTextField: View { diff --git a/homete/Model/Domain/Account/LoginContext.swift b/homete/Views/Components/Environment/LoginContext+Environment.swift similarity index 52% rename from homete/Model/Domain/Account/LoginContext.swift rename to homete/Views/Components/Environment/LoginContext+Environment.swift index 7b03dc58..6bf24d9b 100644 --- a/homete/Model/Domain/Account/LoginContext.swift +++ b/homete/Views/Components/Environment/LoginContext+Environment.swift @@ -1,21 +1,14 @@ // -// LoginContext.swift +// LoginContext+Environment.swift // homete // // Created by 佐藤汰一 on 2025/12/27. // +import HometeDomain import SwiftUI -struct LoginContext: Equatable { - - let account: Account - - /// パートナー登録済みかどうか - var hasCohabitant: Bool { account.cohabitantId != nil } -} - extension EnvironmentValues { - + @Entry var loginContext = LoginContext(account: .init(id: "", userName: "", fcmToken: nil, cohabitantId: nil)) } diff --git a/homete/Views/Components/PreviewUtil/HouseworkUtil.swift b/homete/Views/Components/PreviewUtil/HouseworkUtil.swift index 00efe0c0..8a368869 100644 --- a/homete/Views/Components/PreviewUtil/HouseworkUtil.swift +++ b/homete/Views/Components/PreviewUtil/HouseworkUtil.swift @@ -6,6 +6,7 @@ // import Foundation +import HometeDomain extension HouseworkItem { diff --git a/homete/Views/HomeView/HomeView.swift b/homete/Views/HomeView/HomeView.swift index b7cf8745..74c293bd 100644 --- a/homete/Views/HomeView/HomeView.swift +++ b/homete/Views/HomeView/HomeView.swift @@ -5,6 +5,7 @@ // Created by 佐藤汰一 on 2025/08/11. // +import HometeDomain import SwiftUI struct HomeView: View { diff --git a/homete/Views/HouseworkApproval/Components/HouseworkItemPropertyListContent.swift b/homete/Views/HouseworkApproval/Components/HouseworkItemPropertyListContent.swift index 5914718b..1ad27f2f 100644 --- a/homete/Views/HouseworkApproval/Components/HouseworkItemPropertyListContent.swift +++ b/homete/Views/HouseworkApproval/Components/HouseworkItemPropertyListContent.swift @@ -5,6 +5,7 @@ // Created by 佐藤汰一 on 2025/12/06. // +import HometeDomain import SwiftUI struct HouseworkItemPropertyListContent: View { diff --git a/homete/Views/HouseworkApproval/HouseworkApprovalView.swift b/homete/Views/HouseworkApproval/HouseworkApprovalView.swift index e2306f32..02b240cd 100644 --- a/homete/Views/HouseworkApproval/HouseworkApprovalView.swift +++ b/homete/Views/HouseworkApproval/HouseworkApprovalView.swift @@ -5,6 +5,7 @@ // Created by 佐藤汰一 on 2025/11/23. // +import HometeDomain import SwiftUI struct HouseworkApprovalView: View { diff --git a/homete/Views/HouseworkBoardView/HouseworkBoardView.swift b/homete/Views/HouseworkBoardView/HouseworkBoardView.swift index 27f8d523..f97eb93d 100644 --- a/homete/Views/HouseworkBoardView/HouseworkBoardView.swift +++ b/homete/Views/HouseworkBoardView/HouseworkBoardView.swift @@ -5,6 +5,7 @@ // Created by 佐藤汰一 on 2025/09/06. // +import HometeDomain import SwiftUI struct HouseworkBoardView: View { diff --git a/homete/Views/HouseworkBoardView/SubViews/HouseBoardListRow.swift b/homete/Views/HouseworkBoardView/SubViews/HouseBoardListRow.swift index 3e6fac6c..50826103 100644 --- a/homete/Views/HouseworkBoardView/SubViews/HouseBoardListRow.swift +++ b/homete/Views/HouseworkBoardView/SubViews/HouseBoardListRow.swift @@ -5,6 +5,7 @@ // Created by 佐藤汰一 on 2025/10/28. // +import HometeDomain import SwiftUI struct HouseBoardListRow: View { diff --git a/homete/Views/HouseworkBoardView/SubViews/HouseworkBoardListContent.swift b/homete/Views/HouseworkBoardView/SubViews/HouseworkBoardListContent.swift index a2b67e79..45467497 100644 --- a/homete/Views/HouseworkBoardView/SubViews/HouseworkBoardListContent.swift +++ b/homete/Views/HouseworkBoardView/SubViews/HouseworkBoardListContent.swift @@ -5,6 +5,7 @@ // Created by 佐藤汰一 on 2025/09/06. // +import HometeDomain import SwiftUI struct HouseworkBoardListContent: View { diff --git a/homete/Views/HouseworkBoardView/SubViews/HouseworkBoardSegmentedControl.swift b/homete/Views/HouseworkBoardView/SubViews/HouseworkBoardSegmentedControl.swift index 8c8bbfd4..46f5b67b 100644 --- a/homete/Views/HouseworkBoardView/SubViews/HouseworkBoardSegmentedControl.swift +++ b/homete/Views/HouseworkBoardView/SubViews/HouseworkBoardSegmentedControl.swift @@ -5,6 +5,7 @@ // Created by 佐藤汰一 on 2025/09/06. // +import HometeDomain import SwiftUI struct HouseworkBoardSegmentedControl: View { diff --git a/homete/Views/HouseworkDetailView/HouseworkDetailView.swift b/homete/Views/HouseworkDetailView/HouseworkDetailView.swift index 72eac2e3..cae30be7 100644 --- a/homete/Views/HouseworkDetailView/HouseworkDetailView.swift +++ b/homete/Views/HouseworkDetailView/HouseworkDetailView.swift @@ -5,6 +5,7 @@ // Created by 佐藤汰一 on 2025/11/08. // +import HometeDomain import SwiftUI struct HouseworkDetailView: View { diff --git a/homete/Views/HouseworkDetailView/SubViews/HouseworkDetailActionContent.swift b/homete/Views/HouseworkDetailView/SubViews/HouseworkDetailActionContent.swift index 2c4ce35f..a6c2df50 100644 --- a/homete/Views/HouseworkDetailView/SubViews/HouseworkDetailActionContent.swift +++ b/homete/Views/HouseworkDetailView/SubViews/HouseworkDetailActionContent.swift @@ -5,6 +5,7 @@ // Created by 佐藤汰一 on 2025/11/16. // +import HometeDomain import SwiftUI struct HouseworkDetailActionContent: View { diff --git a/homete/Views/HouseworkDetailView/SubViews/HouseworkDetailItemListContent.swift b/homete/Views/HouseworkDetailView/SubViews/HouseworkDetailItemListContent.swift index 2952ee00..a737a7df 100644 --- a/homete/Views/HouseworkDetailView/SubViews/HouseworkDetailItemListContent.swift +++ b/homete/Views/HouseworkDetailView/SubViews/HouseworkDetailItemListContent.swift @@ -5,6 +5,7 @@ // Created by 佐藤汰一 on 2026/01/04. // +import HometeDomain import SwiftUI struct HouseworkDetailItemListContent: View { diff --git a/homete/Views/RegisterCohabitantView/CohabitantRegistrationView.swift b/homete/Views/RegisterCohabitantView/CohabitantRegistrationView.swift index 840c42f1..69c3e361 100644 --- a/homete/Views/RegisterCohabitantView/CohabitantRegistrationView.swift +++ b/homete/Views/RegisterCohabitantView/CohabitantRegistrationView.swift @@ -6,6 +6,7 @@ // import MultipeerConnectivity +import HometeDomain import SwiftUI struct CohabitantRegistrationView: View { diff --git a/homete/Views/RegisterCohabitantView/SubViews/CohabitantRegistrationSession.swift b/homete/Views/RegisterCohabitantView/SubViews/CohabitantRegistrationSession.swift index 2f6a907c..e15f9a03 100644 --- a/homete/Views/RegisterCohabitantView/SubViews/CohabitantRegistrationSession.swift +++ b/homete/Views/RegisterCohabitantView/SubViews/CohabitantRegistrationSession.swift @@ -6,6 +6,7 @@ // import MultipeerConnectivity +import HometeDomain import SwiftUI struct CohabitantRegistrationSession: View { diff --git a/homete/Views/RegisterCohabitantView/SubViews/ProcessingState/CohabitantRegistrationProcessingFollowerView.swift b/homete/Views/RegisterCohabitantView/SubViews/ProcessingState/CohabitantRegistrationProcessingFollowerView.swift index 5f1c6547..20c7b850 100644 --- a/homete/Views/RegisterCohabitantView/SubViews/ProcessingState/CohabitantRegistrationProcessingFollowerView.swift +++ b/homete/Views/RegisterCohabitantView/SubViews/ProcessingState/CohabitantRegistrationProcessingFollowerView.swift @@ -6,6 +6,7 @@ // import MultipeerConnectivity +import HometeDomain import SwiftUI struct CohabitantRegistrationProcessingFollower: View { diff --git a/homete/Views/RegisterCohabitantView/SubViews/ProcessingState/CohabitantRegistrationProcessingLeader.swift b/homete/Views/RegisterCohabitantView/SubViews/ProcessingState/CohabitantRegistrationProcessingLeader.swift index 6c2a8041..c3eaf924 100644 --- a/homete/Views/RegisterCohabitantView/SubViews/ProcessingState/CohabitantRegistrationProcessingLeader.swift +++ b/homete/Views/RegisterCohabitantView/SubViews/ProcessingState/CohabitantRegistrationProcessingLeader.swift @@ -6,6 +6,7 @@ // import MultipeerConnectivity +import HometeDomain import SwiftUI struct CohabitantRegistrationProcessingLeader: View { diff --git a/homete/Views/RegisterCohabitantView/SubViews/ProcessingState/CohabitantRegistrationProcessingView.swift b/homete/Views/RegisterCohabitantView/SubViews/ProcessingState/CohabitantRegistrationProcessingView.swift index f183305c..7e1ea426 100644 --- a/homete/Views/RegisterCohabitantView/SubViews/ProcessingState/CohabitantRegistrationProcessingView.swift +++ b/homete/Views/RegisterCohabitantView/SubViews/ProcessingState/CohabitantRegistrationProcessingView.swift @@ -7,6 +7,7 @@ import Combine import MultipeerConnectivity +import HometeDomain import SwiftUI struct CohabitantRegistrationProcessingView: View { diff --git a/homete/Views/RegisterCohabitantView/SubViews/ScanningState/CohabitantRegistrationPeersListView.swift b/homete/Views/RegisterCohabitantView/SubViews/ScanningState/CohabitantRegistrationPeersListView.swift index ef8c09d3..4d87b2f2 100644 --- a/homete/Views/RegisterCohabitantView/SubViews/ScanningState/CohabitantRegistrationPeersListView.swift +++ b/homete/Views/RegisterCohabitantView/SubViews/ScanningState/CohabitantRegistrationPeersListView.swift @@ -6,6 +6,7 @@ // import MultipeerConnectivity +import HometeDomain import SwiftUI struct CohabitantRegistrationPeersListView: View { diff --git a/homete/Views/RegisterCohabitantView/SubViews/ScanningState/CohabitantRegistrationScanningStateView.swift b/homete/Views/RegisterCohabitantView/SubViews/ScanningState/CohabitantRegistrationScanningStateView.swift index a5858295..7917e64e 100644 --- a/homete/Views/RegisterCohabitantView/SubViews/ScanningState/CohabitantRegistrationScanningStateView.swift +++ b/homete/Views/RegisterCohabitantView/SubViews/ScanningState/CohabitantRegistrationScanningStateView.swift @@ -6,6 +6,7 @@ // import MultipeerConnectivity +import HometeDomain import SwiftUI struct CohabitantRegistrationScanningStateView: View { diff --git a/homete/Views/RegisterHouseworkView/RegisterHouseworkView.swift b/homete/Views/RegisterHouseworkView/RegisterHouseworkView.swift index cca38464..a664ac48 100644 --- a/homete/Views/RegisterHouseworkView/RegisterHouseworkView.swift +++ b/homete/Views/RegisterHouseworkView/RegisterHouseworkView.swift @@ -5,8 +5,9 @@ // Created by 佐藤汰一 on 2025/09/07. // -import Prefire import FirebaseFunctions +import Prefire +import HometeDomain import SwiftUI struct RegisterHouseworkView: View { diff --git a/homete/Views/RootView/LaunchStateProxy.swift b/homete/Views/RootView/LaunchStateProxy.swift index 3ca23f13..770d5da4 100644 --- a/homete/Views/RootView/LaunchStateProxy.swift +++ b/homete/Views/RootView/LaunchStateProxy.swift @@ -5,6 +5,7 @@ // Created by 佐藤汰一 on 2025/12/27. // +import HometeDomain import SwiftUI struct LaunchStateProxy { diff --git a/homete/Views/RootView/RootView.swift b/homete/Views/RootView/RootView.swift index ec996313..423f9620 100644 --- a/homete/Views/RootView/RootView.swift +++ b/homete/Views/RootView/RootView.swift @@ -6,6 +6,7 @@ // import FirebaseMessaging +import HometeDomain import SwiftUI struct RootView: View { @@ -60,13 +61,21 @@ extension RootView { static func make() -> some View { DependenciesInjectLayer { RootView() - .environment(AccountStore(appDependencies: $0)) - .environment(AccountAuthStore(appDependencies: $0)) + .environment(AccountStore(accountInfoClient: $0.accountInfoClient)) + .environment(AccountAuthStore( + accountAuthClient: $0.accountAuthClient, + analyticsClient: $0.analyticsClient, + signInWithAppleClient: $0.signInWithAppleClient, + nonceGenerationClient: $0.nonceGeneratorClient + )) .environment(HouseworkListStore( houseworkClient: $0.houseworkClient, cohabitantPushNotificationClient: $0.cohabitantPushNotificationClient )) - .environment(CohabitantStore(appDependencies: $0)) + .environment(CohabitantStore( + cohabitantClient: $0.cohabitantClient, + accountInfoClient: $0.accountInfoClient + )) } } } diff --git a/homete/Views/SettingView/SettingView.swift b/homete/Views/SettingView/SettingView.swift index 835a0869..3ca47d3e 100644 --- a/homete/Views/SettingView/SettingView.swift +++ b/homete/Views/SettingView/SettingView.swift @@ -5,6 +5,7 @@ // Created by 佐藤汰一 on 2025/08/11. // +import HometeDomain import SwiftUI struct SettingView: View { @@ -123,6 +124,6 @@ private extension SettingView { #Preview { SettingView() - .environment(AccountAuthStore(appDependencies: .previewValue)) - .environment(AccountStore(appDependencies: .previewValue)) + .environment(AccountAuthStore()) + .environment(AccountStore()) } diff --git a/homete/Views/SettingView/SubViews/SettingMenuItemButton.swift b/homete/Views/SettingView/SubViews/SettingMenuItemButton.swift index c94ef0df..2f0a061a 100644 --- a/homete/Views/SettingView/SubViews/SettingMenuItemButton.swift +++ b/homete/Views/SettingView/SubViews/SettingMenuItemButton.swift @@ -5,6 +5,7 @@ // Created by 佐藤汰一 on 2025/08/11. // +import HometeDomain import SwiftUI struct SettingMenuItemButton: View { diff --git a/homete/Views/TabView/AppTabView.swift b/homete/Views/TabView/AppTabView.swift index bdcda430..354c1fd6 100644 --- a/homete/Views/TabView/AppTabView.swift +++ b/homete/Views/TabView/AppTabView.swift @@ -5,6 +5,7 @@ // Created by 佐藤汰一 on 2025/09/06. // +import HometeDomain import SwiftUI import UserNotifications @@ -116,8 +117,8 @@ extension AppTabView { #Preview { AppTabView() - .environment(AccountStore(appDependencies: .previewValue)) - .environment(AccountAuthStore(appDependencies: .previewValue)) + .environment(AccountStore()) + .environment(AccountAuthStore()) .environment(HouseworkListStore( houseworkClient: .previewValue, cohabitantPushNotificationClient: .previewValue diff --git a/homete/Views/ViewUtilities/Alert/DomainErrorAlertContent.swift b/homete/Views/ViewUtilities/Alert/DomainErrorAlertContent.swift index 40dd2386..0861c2d4 100644 --- a/homete/Views/ViewUtilities/Alert/DomainErrorAlertContent.swift +++ b/homete/Views/ViewUtilities/Alert/DomainErrorAlertContent.swift @@ -5,6 +5,7 @@ // Created by 佐藤汰一 on 2025/11/11. // +import HometeDomain import SwiftUI struct DomainErrorAlertContent { diff --git a/homete/Views/ViewUtilities/Navigation/AppNavigationElement.swift b/homete/Views/ViewUtilities/Navigation/AppNavigationElement.swift index 68f1c069..d09a5d15 100644 --- a/homete/Views/ViewUtilities/Navigation/AppNavigationElement.swift +++ b/homete/Views/ViewUtilities/Navigation/AppNavigationElement.swift @@ -5,6 +5,8 @@ // Created by 佐藤汰一 on 2025/11/08. // +import HometeDomain + enum AppNavigationElement: Hashable { /// 家事詳細画面 case houseworkDetail(item: HouseworkItem) diff --git a/hometeTests/Domain/CohabitantRegistration/ConfirmedRegistrationPeersTest.swift b/hometeTests/Domain/CohabitantRegistration/ConfirmedRegistrationPeersTest.swift deleted file mode 100644 index 476dcb52..00000000 --- a/hometeTests/Domain/CohabitantRegistration/ConfirmedRegistrationPeersTest.swift +++ /dev/null @@ -1,71 +0,0 @@ -// -// ConfirmedRegistrationPeersTest.swift -// hometeTests -// -// Created by 佐藤汰一 on 2025/08/31. -// - -import MultipeerConnectivity -import Testing -@testable import homete - -struct ConfirmedRegistrationPeersTest { - - @Test("PeerIDの表示名の降順で一番最初のPeerIDになるデバイスがリードデバイスになる") - func isLeadPeer_lead_case() throws { - - let connectedPeers: Set = [ - .init(displayName: "BBB"), - .init(displayName: "CCC"), - .init(displayName: "EEE") - ] - let peers = ConfirmedRegistrationPeers(peers: connectedPeers) - - let actual = peers.isLeadPeer( - connectedPeers: connectedPeers, - myPeerID: .init(displayName: "AAA") - ) - - #expect(actual == true) - } - - @Test( - "PeerIDの表示名の降順で一番最初ではない場合は、フォロワーデバイスになる", - arguments: [ - MCPeerID(displayName: "DDD"), - .init(displayName: "FFF"), - .init(displayName: "CCC"), - .init(displayName: "ZZZ") - ] - ) - func isLeadPeer_follower_case(myPeerID: MCPeerID) throws { - - let connectedPeers: Set = [ - .init(displayName: "BBB"), - .init(displayName: "CCC"), - .init(displayName: "EEE") - ] - let peers = ConfirmedRegistrationPeers(peers: connectedPeers) - - let actual = peers.isLeadPeer(connectedPeers: connectedPeers, myPeerID: myPeerID) - - #expect(actual == false) - } - - @Test( - "接続中の全てのデバイスが確認済みでない場合は、まだ登録メンバーが揃っていないのでリードデバイスかどうかを返さない" - ) - func isLeadPeer_not_match_case() throws { - - let confirmedPeers: Set = [ - .init(displayName: "BBB"), - .init(displayName: "CCC") - ] - let connectedPeers: Set = .init(confirmedPeers + [.init(displayName: "EEE")]) - let peers = ConfirmedRegistrationPeers(peers: confirmedPeers) - - let actual = peers.isLeadPeer(connectedPeers: connectedPeers, myPeerID: .init(displayName: "AAA")) - - #expect(actual == nil) - } -}