diff --git a/app-ios/App/App.xctestplan b/app-ios/App/App.xctestplan index 6b80b28d2..a17c7464f 100644 --- a/app-ios/App/App.xctestplan +++ b/app-ios/App/App.xctestplan @@ -37,6 +37,13 @@ "identifier" : "TimetableDetailFeatureTests", "name" : "TimetableDetailFeatureTests" } + }, + { + "target" : { + "containerPath" : "container:", + "identifier" : "FavoriteFeatureTests", + "name" : "FavoriteFeatureTests" + } } ], "version" : 1 diff --git a/app-ios/Package.swift b/app-ios/Package.swift index 21d308fcf..806311f51 100644 --- a/app-ios/Package.swift +++ b/app-ios/Package.swift @@ -29,7 +29,11 @@ let package = Package( .library( name: "AboutFeature", targets: ["AboutFeature"] - ) + ), + .library( + name: "FavoriteFeature", + targets: ["FavoriteFeature"] + ), ], dependencies: [ .package(url: "https://github.com/pointfreeco/swift-composable-architecture.git", exact: "1.10.2"), @@ -40,6 +44,8 @@ let package = Package( .target( name: "App", dependencies: [ + .aboutFeature, + .favoriteFeature, .timetableFeature, .timetableDetailFeature, .tca, @@ -111,6 +117,20 @@ let package = Package( ] ), + .target( + name: "FavoriteFeature", + dependencies: [ + .tca, + ] + ), + .testTarget( + name: "FavoriteFeatureTests", + dependencies: [ + .favoriteFeature, + .tca + ] + ), + // Please run ./gradlew app-ios-shared:assembleSharedXCFramework first .binaryTarget(name: "KmpModule", path: "../app-ios-shared/build/XCFrameworks/debug/shared.xcframework"), ] @@ -133,6 +153,7 @@ extension Target.Dependency { static let timetableDetailFeature: Target.Dependency = "TimetableDetailFeature" static let timetableFeature: Target.Dependency = "TimetableFeature" static let aboutFeature: Target.Dependency = "AboutFeature" + static let favoriteFeature: Target.Dependency = "FavoriteFeature" static let kmpModule: Target.Dependency = "KmpModule" static let firebaseAuth: Target.Dependency = .product(name: "FirebaseAuth", package: "firebase-ios-sdk") diff --git a/app-ios/Sources/App/RootReducer.swift b/app-ios/Sources/App/RootReducer.swift index f38402681..c89e23962 100644 --- a/app-ios/Sources/App/RootReducer.swift +++ b/app-ios/Sources/App/RootReducer.swift @@ -1,4 +1,6 @@ +import AboutFeature import ComposableArchitecture +import FavoriteFeature import TimetableFeature @Reducer @@ -9,19 +11,27 @@ public struct RootReducer { public struct State: Equatable { public var appDelegate: AppDelegateReducer.State public var timetable: TimetableReducer.State + public var favorite: FavoriteReducer.State + public var about: AboutReducer.State public init( appDelegate: AppDelegateReducer.State = .init(), - timetable: TimetableReducer.State = .init() + timetable: TimetableReducer.State = .init(), + favorite: FavoriteReducer.State = .init(), + about: AboutReducer.State = .init() ) { self.appDelegate = appDelegate self.timetable = timetable + self.favorite = favorite + self.about = about } } public enum Action { case appDelegate(AppDelegateReducer.Action) case timetable(TimetableReducer.Action) + case favorite(FavoriteReducer.Action) + case about(AboutReducer.Action) } public var body: some ReducerOf { @@ -31,5 +41,11 @@ public struct RootReducer { Scope(state: \.timetable, action: \.timetable) { TimetableReducer() } + Scope(state: \.favorite, action: \.favorite) { + FavoriteReducer() + } + Scope(state: \.about, action: \.about) { + AboutReducer() + } } } diff --git a/app-ios/Sources/App/RootView.swift b/app-ios/Sources/App/RootView.swift index 382a1223c..abd4ab468 100644 --- a/app-ios/Sources/App/RootView.swift +++ b/app-ios/Sources/App/RootView.swift @@ -1,3 +1,5 @@ +import AboutFeature +import FavoriteFeature import ComposableArchitecture import SwiftUI import TimetableFeature @@ -43,23 +45,33 @@ public struct RootView: View { ) } - Text("Favorite Feature") - .tag(Tab.favorite) - .tabItem { - Label( - title: { Text("Favorite") }, - icon: { Image(systemName: "42.circle") } - ) - } + FavoriteScreen( + store: store.scope( + state: \.favorite, + action: \.favorite + ) + ) + .tag(Tab.favorite) + .tabItem { + Label( + title: { Text("Favorite") }, + icon: { Image(systemName: "42.circle") } + ) + } - Text("About Feature") - .tag(Tab.about) - .tabItem { - Label( - title: { Text("About") }, - icon: { Image(systemName: "42.circle") } - ) - } + AboutView( + store: store.scope( + state: \.about, + action: \.about + ) + ) + .tag(Tab.about) + .tabItem { + Label( + title: { Text("About") }, + icon: { Image(systemName: "42.circle") } + ) + } Text("ID Card Feature") .tag(Tab.idCard) diff --git a/app-ios/Sources/FavoriteFeature/FavoriteReducer.swift b/app-ios/Sources/FavoriteFeature/FavoriteReducer.swift new file mode 100644 index 000000000..4c089cc73 --- /dev/null +++ b/app-ios/Sources/FavoriteFeature/FavoriteReducer.swift @@ -0,0 +1,30 @@ +import ComposableArchitecture + +@Reducer +public struct FavoriteReducer { + public init() { } + + @ObservableState + public struct State: Equatable { + public var text: String + + public init(text: String = "") { + self.text = text + } + } + + public enum Action { + case onAppear + } + + public var body: some ReducerOf { + Reduce { state, action in + switch action { + case .onAppear: + state.text = "Favorite Feature" + return .none + } + } + } +} + diff --git a/app-ios/Sources/FavoriteFeature/FavoriteScreen.swift b/app-ios/Sources/FavoriteFeature/FavoriteScreen.swift new file mode 100644 index 000000000..eef5d6d7a --- /dev/null +++ b/app-ios/Sources/FavoriteFeature/FavoriteScreen.swift @@ -0,0 +1,26 @@ +import ComposableArchitecture +import SwiftUI + +public struct FavoriteScreen: View { + private let store: StoreOf + + public init(store: StoreOf) { + self.store = store + } + + public var body: some View { + Text(store.text) + .onAppear { + store.send(.onAppear) + } + } +} + +#Preview { + FavoriteScreen( + store: .init( + initialState: .init(), + reducer: {} + ) + ) +} diff --git a/app-ios/Tests/FavoriteFeatureTests/FavoriteTests.swift b/app-ios/Tests/FavoriteFeatureTests/FavoriteTests.swift new file mode 100644 index 000000000..4cbde630c --- /dev/null +++ b/app-ios/Tests/FavoriteFeatureTests/FavoriteTests.swift @@ -0,0 +1,16 @@ +import XCTest +import ComposableArchitecture +@testable import FavoriteFeature + +final class FavoriteTests: XCTestCase { + @MainActor func testExample() async throws { + let store = TestStore( + initialState: FavoriteReducer.State(text: "Test") + ) { + FavoriteReducer() + } + await store.send(.onAppear) { + $0.text = "Favorite Feature" + } + } +}