From ea834e2a6e3f2c63eb986e8579e3a0511f372bbe Mon Sep 17 00:00:00 2001 From: Jihyun247 Date: Fri, 9 Aug 2024 23:26:16 +0900 Subject: [PATCH 01/14] =?UTF-8?q?fix:=20OnboardingFeature=20interface=20?= =?UTF-8?q?=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../OnboardingFeature/Sources/OnboardingView.swift | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 Projects/Feature/OnboardingFeature/Sources/OnboardingView.swift diff --git a/Projects/Feature/OnboardingFeature/Sources/OnboardingView.swift b/Projects/Feature/OnboardingFeature/Sources/OnboardingView.swift new file mode 100644 index 0000000..ce74d66 --- /dev/null +++ b/Projects/Feature/OnboardingFeature/Sources/OnboardingView.swift @@ -0,0 +1,9 @@ +// +// OnboardingView.swift +// OnboardingFeature +// +// Created by 김지현 on 8/9/24. +// Copyright © 2024 PomoNyang. All rights reserved. +// + +import Foundation From 872933fdee3cf3f937c7334df55a30d47990528c Mon Sep 17 00:00:00 2001 From: Jihyun247 Date: Sat, 10 Aug 2024 01:06:42 +0900 Subject: [PATCH 02/14] =?UTF-8?q?feauture:=20onboarding=20Ui=20=ED=8B=80?= =?UTF-8?q?=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Sources/Onboarding/OnboardingCore.swift | 2 +- .../Sources/Onboarding/OnboardingItem.swift | 1 - .../OnboardingFeature/Sources/OnboardingView.swift | 9 --------- 3 files changed, 1 insertion(+), 11 deletions(-) delete mode 100644 Projects/Feature/OnboardingFeature/Sources/OnboardingView.swift diff --git a/Projects/Feature/OnboardingFeature/Sources/Onboarding/OnboardingCore.swift b/Projects/Feature/OnboardingFeature/Sources/Onboarding/OnboardingCore.swift index d71336e..2bac53e 100644 --- a/Projects/Feature/OnboardingFeature/Sources/Onboarding/OnboardingCore.swift +++ b/Projects/Feature/OnboardingFeature/Sources/Onboarding/OnboardingCore.swift @@ -122,7 +122,7 @@ public struct OnboardingCore { state.currentItemID = state.fakedData[1].id.uuidString return .none - default: + case .binding: return .none } } diff --git a/Projects/Feature/OnboardingFeature/Sources/Onboarding/OnboardingItem.swift b/Projects/Feature/OnboardingFeature/Sources/Onboarding/OnboardingItem.swift index e38a710..f5eb5a9 100644 --- a/Projects/Feature/OnboardingFeature/Sources/Onboarding/OnboardingItem.swift +++ b/Projects/Feature/OnboardingFeature/Sources/Onboarding/OnboardingItem.swift @@ -31,4 +31,3 @@ let OnboardingItemsData: [OnboardingItem] = [ title: "집중과 휴식 반복을 통해 몰입을 관리해요", subTitle: "일정 시간 집중과 휴식을 반복해 번아웃을 방지하고\n짧은 시간의 몰입을 경험해보세요.") ] - diff --git a/Projects/Feature/OnboardingFeature/Sources/OnboardingView.swift b/Projects/Feature/OnboardingFeature/Sources/OnboardingView.swift deleted file mode 100644 index ce74d66..0000000 --- a/Projects/Feature/OnboardingFeature/Sources/OnboardingView.swift +++ /dev/null @@ -1,9 +0,0 @@ -// -// OnboardingView.swift -// OnboardingFeature -// -// Created by 김지현 on 8/9/24. -// Copyright © 2024 PomoNyang. All rights reserved. -// - -import Foundation From b893ab0b86d41f248659a335ec5f461b98c11c30 Mon Sep 17 00:00:00 2001 From: Jihyun247 Date: Wed, 14 Aug 2024 03:21:02 +0900 Subject: [PATCH 03/14] feature: inifinite carousel --- .../Sources/Onboarding/OnboardingItem.swift | 34 +++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/Projects/Feature/OnboardingFeature/Sources/Onboarding/OnboardingItem.swift b/Projects/Feature/OnboardingFeature/Sources/Onboarding/OnboardingItem.swift index f5eb5a9..dcb7276 100644 --- a/Projects/Feature/OnboardingFeature/Sources/Onboarding/OnboardingItem.swift +++ b/Projects/Feature/OnboardingFeature/Sources/Onboarding/OnboardingItem.swift @@ -17,6 +17,15 @@ public struct OnboardingItem: Equatable, Identifiable { let subTitle: String } +func getIndex( + item: OnboardingItem +) -> Int { + let index = OnboardingItemsData.firstIndex { currentItem in + return currentItem.title == item.title + } ?? 0 + return index +} + let OnboardingItemsData: [OnboardingItem] = [ OnboardingItem( image: Image(systemName: "star"), @@ -31,3 +40,28 @@ let OnboardingItemsData: [OnboardingItem] = [ title: "집중과 휴식 반복을 통해 몰입을 관리해요", subTitle: "일정 시간 집중과 휴식을 반복해 번아웃을 방지하고\n짧은 시간의 몰입을 경험해보세요.") ] + +public struct RandomAccessEquatableItems: RandomAccessCollection, Equatable where Element: Equatable { + var elements: [Element] + public init(elements: [Element]) { + self.elements = elements + } + + public var startIndex: Int { elements.startIndex } + public var endIndex: Int { elements.endIndex } + + public subscript(position: Int) -> Element { + return elements[position] + } + + public static func == (lhs: RandomAccessEquatableItems, rhs: RandomAccessEquatableItems) -> Bool { + guard lhs.count == rhs.count else { return false } + for (index, element) in lhs.enumerated() { + if element != rhs[index] { + return false + } + } + return true + } +} + From 5a2ed53eb89e49d0924d6cc25f1bdfebefd00005 Mon Sep 17 00:00:00 2001 From: Jihyun247 Date: Wed, 14 Aug 2024 05:34:22 +0900 Subject: [PATCH 04/14] feature: timer infinite carousel --- .../Sources/Onboarding/OnboardingItem.swift | 9 --------- 1 file changed, 9 deletions(-) diff --git a/Projects/Feature/OnboardingFeature/Sources/Onboarding/OnboardingItem.swift b/Projects/Feature/OnboardingFeature/Sources/Onboarding/OnboardingItem.swift index dcb7276..173ae17 100644 --- a/Projects/Feature/OnboardingFeature/Sources/Onboarding/OnboardingItem.swift +++ b/Projects/Feature/OnboardingFeature/Sources/Onboarding/OnboardingItem.swift @@ -17,15 +17,6 @@ public struct OnboardingItem: Equatable, Identifiable { let subTitle: String } -func getIndex( - item: OnboardingItem -) -> Int { - let index = OnboardingItemsData.firstIndex { currentItem in - return currentItem.title == item.title - } ?? 0 - return index -} - let OnboardingItemsData: [OnboardingItem] = [ OnboardingItem( image: Image(systemName: "star"), From eb4766dabb41ef5370c8d45a2518704fc1331916 Mon Sep 17 00:00:00 2001 From: Jihyun247 Date: Wed, 14 Aug 2024 23:27:58 +0900 Subject: [PATCH 05/14] feature: select cat navigation --- .../Sources/Onboarding/OnboardingCore.swift | 10 ++++++ .../Sources/Onboarding/OnboardingView.swift | 17 ++++++--- .../Sources/SelectCat/SelectCatCore.swift | 34 ++++++++++++++++++ .../Sources/SelectCat/SelectCatView.swift | 35 +++++++++++++++++++ 4 files changed, 91 insertions(+), 5 deletions(-) create mode 100644 Projects/Feature/OnboardingFeature/Sources/SelectCat/SelectCatCore.swift create mode 100644 Projects/Feature/OnboardingFeature/Sources/SelectCat/SelectCatView.swift diff --git a/Projects/Feature/OnboardingFeature/Sources/Onboarding/OnboardingCore.swift b/Projects/Feature/OnboardingFeature/Sources/Onboarding/OnboardingCore.swift index 2bac53e..2cbc138 100644 --- a/Projects/Feature/OnboardingFeature/Sources/Onboarding/OnboardingCore.swift +++ b/Projects/Feature/OnboardingFeature/Sources/Onboarding/OnboardingCore.swift @@ -20,18 +20,21 @@ public struct OnboardingCore { var width: CGFloat = .zero var currentIdx: Int = 0 var currentItemID: String = "" + @Presents var selectCat: SelectCatCore.State? } public enum Action: BindableAction { case onApear case calculateOffset(CGFloat, OnboardingItem) case dragStart + case tapStartButton case _timerStart case _timerEnd case _timerTicked case _nextPage(Int) case _resetTofront case binding(BindingAction) + case selectCat(PresentationAction) } public init() { } @@ -42,6 +45,9 @@ public struct OnboardingCore { public var body: some ReducerOf { BindingReducer() Reduce(self.core) + .ifLet(\.$selectCat, action: \.selectCat) { + SelectCatCore() + } } private func core(_ state: inout State, _ action: Action) -> EffectOf { @@ -90,6 +96,10 @@ public struct OnboardingCore { timerStartAction ) + case .tapStartButton: + state.selectCat = SelectCatCore.State() + return .run { send in await send(._timerEnd) } + case ._timerStart: return .run { send in for await _ in self.clock.timer(interval: .seconds(3)) { diff --git a/Projects/Feature/OnboardingFeature/Sources/Onboarding/OnboardingView.swift b/Projects/Feature/OnboardingFeature/Sources/Onboarding/OnboardingView.swift index 65e52b6..bb3c87b 100644 --- a/Projects/Feature/OnboardingFeature/Sources/Onboarding/OnboardingView.swift +++ b/Projects/Feature/OnboardingFeature/Sources/Onboarding/OnboardingView.swift @@ -19,9 +19,7 @@ public struct OnboardingView: View { } public var body: some View { - ZStack { - Alias.Color.Background.primary - .ignoresSafeArea() + NavigationStack { VStack { Spacer() VStack(spacing: 0) { @@ -54,15 +52,24 @@ public struct OnboardingView: View { .padding(.vertical, 32) Button(title: "시작하기") { - // 다음 뷰 이동 + store.send(.tapStartButton) } .buttonStyle(.box(level: .primary, size: .large, width: .medium)) .padding(.top, 16) } Spacer() } + .background { + Alias.Color.Background.primary + .ignoresSafeArea() + } + .onAppear { store.send(.onApear) } + .navigationDestination( + item: $store.scope(state: \.selectCat, action: \.selectCat) + ) { store in + SelectCatView(store: store) + } } - .onAppear { store.send(.onApear) } .background { GeometryReader { geometry in Color.clear diff --git a/Projects/Feature/OnboardingFeature/Sources/SelectCat/SelectCatCore.swift b/Projects/Feature/OnboardingFeature/Sources/SelectCat/SelectCatCore.swift new file mode 100644 index 0000000..82b22fe --- /dev/null +++ b/Projects/Feature/OnboardingFeature/Sources/SelectCat/SelectCatCore.swift @@ -0,0 +1,34 @@ +// +// SelectCatCore.swift +// OnboardingFeature +// +// Created by 김지현 on 8/14/24. +// Copyright © 2024 PomoNyang. All rights reserved. +// + +import ComposableArchitecture + +@Reducer +public struct SelectCatCore { + @ObservableState + public struct State: Equatable { + var cat: String = "" + } + + public enum Action { + case onAppear + } + + public init() {} + + public var body: some ReducerOf { + Reduce(self.core) + } + + private func core(state: inout State, action: Action) -> EffectOf { + switch action { + case .onAppear: + return .none + } + } +} diff --git a/Projects/Feature/OnboardingFeature/Sources/SelectCat/SelectCatView.swift b/Projects/Feature/OnboardingFeature/Sources/SelectCat/SelectCatView.swift new file mode 100644 index 0000000..077ede5 --- /dev/null +++ b/Projects/Feature/OnboardingFeature/Sources/SelectCat/SelectCatView.swift @@ -0,0 +1,35 @@ +// +// SelectCatView.swift +// OnboardingFeature +// +// Created by 김지현 on 8/14/24. +// Copyright © 2024 PomoNyang. All rights reserved. +// + +import SwiftUI + +import DesignSystem + +import ComposableArchitecture + +public struct SelectCatView: View { + @Bindable var store: StoreOf + + public init(store: StoreOf) { + self.store = store + } + + public var body: some View { + NavigationContainer( + title: Text("고양이 선택"), + style: .navigation, + navBackground: .clear + ) { + Text("SELECT CAT VIEW") + } + .background { + Alias.Color.Background.primary + .ignoresSafeArea() + } + } +} From 2323a0e57af807a82920d697482eabd5c5413b5a Mon Sep 17 00:00:00 2001 From: Jihyun247 Date: Thu, 15 Aug 2024 00:51:00 +0900 Subject: [PATCH 06/14] feature: cat factory --- .../Sources/SelectCat/CatFactory.swift | 60 +++++++++++++++++++ .../16/16_focus.imageset/16_focus.svg | 4 ++ .../16/16_focus.imageset/Contents.json | 21 +++++++ .../16/16_heart.imageset/16_heart.svg | 4 ++ .../16/16_heart.imageset/Contents.json | 21 +++++++ .../16/16_star.imageset/16_star.svg | 3 + .../16/16_star.imageset/Contents.json | 21 +++++++ .../Resources/Image.xcassets/16/Contents.json | 6 ++ .../24_arrow_left_primary.svg | 0 .../Contents.json | 0 .../24_cancel_primary.svg | 0 .../24_cancel_primary.imageset/Contents.json | 0 .../Resources/Image.xcassets/24/Contents.json | 6 ++ 13 files changed, 146 insertions(+) create mode 100644 Projects/Feature/OnboardingFeature/Sources/SelectCat/CatFactory.swift create mode 100644 Projects/Shared/DesignSystem/Resources/Image.xcassets/16/16_focus.imageset/16_focus.svg create mode 100644 Projects/Shared/DesignSystem/Resources/Image.xcassets/16/16_focus.imageset/Contents.json create mode 100644 Projects/Shared/DesignSystem/Resources/Image.xcassets/16/16_heart.imageset/16_heart.svg create mode 100644 Projects/Shared/DesignSystem/Resources/Image.xcassets/16/16_heart.imageset/Contents.json create mode 100644 Projects/Shared/DesignSystem/Resources/Image.xcassets/16/16_star.imageset/16_star.svg create mode 100644 Projects/Shared/DesignSystem/Resources/Image.xcassets/16/16_star.imageset/Contents.json create mode 100644 Projects/Shared/DesignSystem/Resources/Image.xcassets/16/Contents.json rename Projects/Shared/DesignSystem/Resources/Image.xcassets/{ => 24}/24_arrow_left_primary.imageset/24_arrow_left_primary.svg (100%) rename Projects/Shared/DesignSystem/Resources/Image.xcassets/{ => 24}/24_arrow_left_primary.imageset/Contents.json (100%) rename Projects/Shared/DesignSystem/Resources/Image.xcassets/{ => 24}/24_cancel_primary.imageset/24_cancel_primary.svg (100%) rename Projects/Shared/DesignSystem/Resources/Image.xcassets/{ => 24}/24_cancel_primary.imageset/Contents.json (100%) create mode 100644 Projects/Shared/DesignSystem/Resources/Image.xcassets/24/Contents.json diff --git a/Projects/Feature/OnboardingFeature/Sources/SelectCat/CatFactory.swift b/Projects/Feature/OnboardingFeature/Sources/SelectCat/CatFactory.swift new file mode 100644 index 0000000..c42205c --- /dev/null +++ b/Projects/Feature/OnboardingFeature/Sources/SelectCat/CatFactory.swift @@ -0,0 +1,60 @@ +// +// CatFactory.swift +// OnboardingFeature +// +// Created by 김지현 on 8/15/24. +// Copyright © 2024 PomoNyang. All rights reserved. +// + +import SwiftUI + +import DesignSystem + +public enum CatType { + case cheese, black, calico +} + +public protocol CatFactoryProtocol { + var keyword: String { get } + var keywordImage: Image { get } + var typeName: String { get } + var catImage: Image { get } + var pushNotificationTitle: String { get } +} + +public struct CheeseCat: CatFactoryProtocol { + public var keyword: String = "응원" + public var keywordImage: Image = DesignSystemAsset.Image._16Star.swiftUIImage + public var typeName: String = "치즈냥" + public var catImage: Image = Image(systemName: "star.fill") + public var pushNotificationTitle: String = "어디갔냐옹..." +} + +public struct BlackCat: CatFactoryProtocol { + public var keyword: String = "긍정" + public var keywordImage: Image = DesignSystemAsset.Image._16Heart.swiftUIImage + public var typeName: String = "까만냥" + public var catImage: Image = Image(systemName: "star") + public var pushNotificationTitle: String = "어디갔냐옹..." +} + +public struct CalicoCat: CatFactoryProtocol { + public var keyword: String = "자극" + public var keywordImage: Image = DesignSystemAsset.Image._16Focus.swiftUIImage + public var typeName: String = "삼색냥" + public var catImage: Image = Image(systemName: "star.fill") + public var pushNotificationTitle: String = "내가 여기있는데 어디갔냐옹!" +} + +public struct CatFactory { + public static func makeCat(type: CatType) -> CatFactoryProtocol { + switch type { + case .cheese: + return CheeseCat() + case .black: + return BlackCat() + case .calico: + return CalicoCat() + } + } +} diff --git a/Projects/Shared/DesignSystem/Resources/Image.xcassets/16/16_focus.imageset/16_focus.svg b/Projects/Shared/DesignSystem/Resources/Image.xcassets/16/16_focus.imageset/16_focus.svg new file mode 100644 index 0000000..66d94b3 --- /dev/null +++ b/Projects/Shared/DesignSystem/Resources/Image.xcassets/16/16_focus.imageset/16_focus.svg @@ -0,0 +1,4 @@ + + + + diff --git a/Projects/Shared/DesignSystem/Resources/Image.xcassets/16/16_focus.imageset/Contents.json b/Projects/Shared/DesignSystem/Resources/Image.xcassets/16/16_focus.imageset/Contents.json new file mode 100644 index 0000000..5eafda9 --- /dev/null +++ b/Projects/Shared/DesignSystem/Resources/Image.xcassets/16/16_focus.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "16_focus.svg", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Projects/Shared/DesignSystem/Resources/Image.xcassets/16/16_heart.imageset/16_heart.svg b/Projects/Shared/DesignSystem/Resources/Image.xcassets/16/16_heart.imageset/16_heart.svg new file mode 100644 index 0000000..9ed635e --- /dev/null +++ b/Projects/Shared/DesignSystem/Resources/Image.xcassets/16/16_heart.imageset/16_heart.svg @@ -0,0 +1,4 @@ + + + + diff --git a/Projects/Shared/DesignSystem/Resources/Image.xcassets/16/16_heart.imageset/Contents.json b/Projects/Shared/DesignSystem/Resources/Image.xcassets/16/16_heart.imageset/Contents.json new file mode 100644 index 0000000..f76510e --- /dev/null +++ b/Projects/Shared/DesignSystem/Resources/Image.xcassets/16/16_heart.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "16_heart.svg", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Projects/Shared/DesignSystem/Resources/Image.xcassets/16/16_star.imageset/16_star.svg b/Projects/Shared/DesignSystem/Resources/Image.xcassets/16/16_star.imageset/16_star.svg new file mode 100644 index 0000000..d79a658 --- /dev/null +++ b/Projects/Shared/DesignSystem/Resources/Image.xcassets/16/16_star.imageset/16_star.svg @@ -0,0 +1,3 @@ + + + diff --git a/Projects/Shared/DesignSystem/Resources/Image.xcassets/16/16_star.imageset/Contents.json b/Projects/Shared/DesignSystem/Resources/Image.xcassets/16/16_star.imageset/Contents.json new file mode 100644 index 0000000..f59498c --- /dev/null +++ b/Projects/Shared/DesignSystem/Resources/Image.xcassets/16/16_star.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "16_star.svg", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Projects/Shared/DesignSystem/Resources/Image.xcassets/16/Contents.json b/Projects/Shared/DesignSystem/Resources/Image.xcassets/16/Contents.json new file mode 100644 index 0000000..73c0059 --- /dev/null +++ b/Projects/Shared/DesignSystem/Resources/Image.xcassets/16/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Projects/Shared/DesignSystem/Resources/Image.xcassets/24_arrow_left_primary.imageset/24_arrow_left_primary.svg b/Projects/Shared/DesignSystem/Resources/Image.xcassets/24/24_arrow_left_primary.imageset/24_arrow_left_primary.svg similarity index 100% rename from Projects/Shared/DesignSystem/Resources/Image.xcassets/24_arrow_left_primary.imageset/24_arrow_left_primary.svg rename to Projects/Shared/DesignSystem/Resources/Image.xcassets/24/24_arrow_left_primary.imageset/24_arrow_left_primary.svg diff --git a/Projects/Shared/DesignSystem/Resources/Image.xcassets/24_arrow_left_primary.imageset/Contents.json b/Projects/Shared/DesignSystem/Resources/Image.xcassets/24/24_arrow_left_primary.imageset/Contents.json similarity index 100% rename from Projects/Shared/DesignSystem/Resources/Image.xcassets/24_arrow_left_primary.imageset/Contents.json rename to Projects/Shared/DesignSystem/Resources/Image.xcassets/24/24_arrow_left_primary.imageset/Contents.json diff --git a/Projects/Shared/DesignSystem/Resources/Image.xcassets/24_cancel_primary.imageset/24_cancel_primary.svg b/Projects/Shared/DesignSystem/Resources/Image.xcassets/24/24_cancel_primary.imageset/24_cancel_primary.svg similarity index 100% rename from Projects/Shared/DesignSystem/Resources/Image.xcassets/24_cancel_primary.imageset/24_cancel_primary.svg rename to Projects/Shared/DesignSystem/Resources/Image.xcassets/24/24_cancel_primary.imageset/24_cancel_primary.svg diff --git a/Projects/Shared/DesignSystem/Resources/Image.xcassets/24_cancel_primary.imageset/Contents.json b/Projects/Shared/DesignSystem/Resources/Image.xcassets/24/24_cancel_primary.imageset/Contents.json similarity index 100% rename from Projects/Shared/DesignSystem/Resources/Image.xcassets/24_cancel_primary.imageset/Contents.json rename to Projects/Shared/DesignSystem/Resources/Image.xcassets/24/24_cancel_primary.imageset/Contents.json diff --git a/Projects/Shared/DesignSystem/Resources/Image.xcassets/24/Contents.json b/Projects/Shared/DesignSystem/Resources/Image.xcassets/24/Contents.json new file mode 100644 index 0000000..73c0059 --- /dev/null +++ b/Projects/Shared/DesignSystem/Resources/Image.xcassets/24/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} From 5d20b9c5c2aafc1da4fb0d110faa6e3138901f47 Mon Sep 17 00:00:00 2001 From: Jihyun247 Date: Thu, 15 Aug 2024 16:05:37 +0900 Subject: [PATCH 07/14] =?UTF-8?q?feature:=20cat=20factory=20=EC=98=88?= =?UTF-8?q?=EC=8B=9C=ED=91=B8=EC=8B=9C=EC=95=8C=EB=A6=BC=EB=B7=B0=20wip?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Sources/SelectCat/CatFactory.swift | 4 +- .../Sources/SelectCat/SelectCatCore.swift | 9 ++- .../Sources/SelectCat/SelectCatView.swift | 64 ++++++++++++++++++- 3 files changed, 71 insertions(+), 6 deletions(-) diff --git a/Projects/Feature/OnboardingFeature/Sources/SelectCat/CatFactory.swift b/Projects/Feature/OnboardingFeature/Sources/SelectCat/CatFactory.swift index c42205c..128f47d 100644 --- a/Projects/Feature/OnboardingFeature/Sources/SelectCat/CatFactory.swift +++ b/Projects/Feature/OnboardingFeature/Sources/SelectCat/CatFactory.swift @@ -14,7 +14,7 @@ public enum CatType { case cheese, black, calico } -public protocol CatFactoryProtocol { +public protocol CatFactoryProtocol where Self: Equatable { var keyword: String { get } var keywordImage: Image { get } var typeName: String { get } @@ -47,7 +47,7 @@ public struct CalicoCat: CatFactoryProtocol { } public struct CatFactory { - public static func makeCat(type: CatType) -> CatFactoryProtocol { + public static func makeCat(type: CatType) -> any CatFactoryProtocol { switch type { case .cheese: return CheeseCat() diff --git a/Projects/Feature/OnboardingFeature/Sources/SelectCat/SelectCatCore.swift b/Projects/Feature/OnboardingFeature/Sources/SelectCat/SelectCatCore.swift index 82b22fe..b19bdd3 100644 --- a/Projects/Feature/OnboardingFeature/Sources/SelectCat/SelectCatCore.swift +++ b/Projects/Feature/OnboardingFeature/Sources/SelectCat/SelectCatCore.swift @@ -12,7 +12,14 @@ import ComposableArchitecture public struct SelectCatCore { @ObservableState public struct State: Equatable { - var cat: String = "" + var catType: CatType? + var cat: (any CatFactoryProtocol)? { + if let catType = catType { + return CatFactory.makeCat(type: catType) + } else { + return nil + } + } } public enum Action { diff --git a/Projects/Feature/OnboardingFeature/Sources/SelectCat/SelectCatView.swift b/Projects/Feature/OnboardingFeature/Sources/SelectCat/SelectCatView.swift index 077ede5..c80cb78 100644 --- a/Projects/Feature/OnboardingFeature/Sources/SelectCat/SelectCatView.swift +++ b/Projects/Feature/OnboardingFeature/Sources/SelectCat/SelectCatView.swift @@ -14,18 +14,38 @@ import ComposableArchitecture public struct SelectCatView: View { @Bindable var store: StoreOf - + public init(store: StoreOf) { self.store = store } - + public var body: some View { NavigationContainer( title: Text("고양이 선택"), style: .navigation, navBackground: .clear ) { - Text("SELECT CAT VIEW") + VStack { + VStack(spacing: Alias.Spacing.xSmall) { + Text("어떤 고양이와 함께할까요?") + .font(Typography.header3) + .foregroundStyle(Alias.Color.Text.primary) + Text("언제든지 다른 고양이를 선택할 수 있어요") + .font(Typography.bodyR) + .foregroundStyle(Alias.Color.Text.secondary) + } + + Spacer(minLength: Alias.Spacing.xLarge) + + VStack { + CatPushNotificationExampleView(cat: store.cat) + } + + Button(title: "이 고양이와 함께하기") { + // click! + } + .buttonStyle(.box(level: .primary, size: .large, width: .medium)) + } } .background { Alias.Color.Background.primary @@ -33,3 +53,41 @@ public struct SelectCatView: View { } } } + +struct CatPushNotificationExampleView: View { + var cat: (any CatFactoryProtocol)? + var body: some View { + ZStack { + RoundedRectangle(cornerRadius: Alias.BorderRadius.xSmall,style: .circular) + .foregroundStyle(Alias.Color.Background.secondary) + + if let cat = cat { + HStack { + Image(systemName: "star.fill") + VStack(spacing: 0) { + Text("모하냥") + .font(Typography.bodyR) + .foregroundStyle(Alias.Color.Text.primary) + Text(cat.pushNotificationTitle) + .font(Typography.subBodyR) + .foregroundStyle(Alias.Color.Text.primary) + } + Spacer() + VStack(spacing: 0) { + Text("지금") + .font(Typography.subBodyR) + .foregroundStyle(Alias.Color.Text.secondary) + Spacer() + } + } + .padding(.all, Alias.Spacing.large) + } else { + Text("고양이를 선택하면\n딴 짓 방해알림 예시를 보여드려요") + .font(Typography.bodyR) + .foregroundStyle(Alias.Color.Text.tertiary) + .multilineTextAlignment(.center) + .padding(.all, Alias.Spacing.large) + } + } + } +} From 0c7df690c7c4cafaeb1aceeb2b9929f1146fa4e5 Mon Sep 17 00:00:00 2001 From: Jihyun247 Date: Thu, 15 Aug 2024 19:43:37 +0900 Subject: [PATCH 08/14] =?UTF-8?q?feature:=20=EA=B3=A0=EC=96=91=EC=9D=B4=20?= =?UTF-8?q?=EC=84=A0=ED=83=9D=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Sources/Onboarding/OnboardingCore.swift | 3 + .../Sources/SelectCat/CatFactory.swift | 91 ++++++++++++------- .../Sources/SelectCat/SelectCatCore.swift | 28 ++++-- .../Sources/SelectCat/SelectCatView.swift | 89 ++++++++++++------ 4 files changed, 137 insertions(+), 74 deletions(-) diff --git a/Projects/Feature/OnboardingFeature/Sources/Onboarding/OnboardingCore.swift b/Projects/Feature/OnboardingFeature/Sources/Onboarding/OnboardingCore.swift index 2cbc138..eb1f105 100644 --- a/Projects/Feature/OnboardingFeature/Sources/Onboarding/OnboardingCore.swift +++ b/Projects/Feature/OnboardingFeature/Sources/Onboarding/OnboardingCore.swift @@ -134,6 +134,9 @@ public struct OnboardingCore { case .binding: return .none + + case .selectCat: + return .none } } } diff --git a/Projects/Feature/OnboardingFeature/Sources/SelectCat/CatFactory.swift b/Projects/Feature/OnboardingFeature/Sources/SelectCat/CatFactory.swift index 128f47d..fb3869c 100644 --- a/Projects/Feature/OnboardingFeature/Sources/SelectCat/CatFactory.swift +++ b/Projects/Feature/OnboardingFeature/Sources/SelectCat/CatFactory.swift @@ -10,51 +10,72 @@ import SwiftUI import DesignSystem -public enum CatType { - case cheese, black, calico -} - -public protocol CatFactoryProtocol where Self: Equatable { - var keyword: String { get } +public protocol CatFactoryProtocol { + var keyword: LocalizedStringKey { get } var keywordImage: Image { get } - var typeName: String { get } + var typeName: LocalizedStringKey { get } var catImage: Image { get } var pushNotificationTitle: String { get } } -public struct CheeseCat: CatFactoryProtocol { - public var keyword: String = "응원" - public var keywordImage: Image = DesignSystemAsset.Image._16Star.swiftUIImage - public var typeName: String = "치즈냥" - public var catImage: Image = Image(systemName: "star.fill") - public var pushNotificationTitle: String = "어디갔냐옹..." -} - -public struct BlackCat: CatFactoryProtocol { - public var keyword: String = "긍정" - public var keywordImage: Image = DesignSystemAsset.Image._16Heart.swiftUIImage - public var typeName: String = "까만냥" - public var catImage: Image = Image(systemName: "star") - public var pushNotificationTitle: String = "어디갔냐옹..." -} - -public struct CalicoCat: CatFactoryProtocol { - public var keyword: String = "자극" - public var keywordImage: Image = DesignSystemAsset.Image._16Focus.swiftUIImage - public var typeName: String = "삼색냥" - public var catImage: Image = Image(systemName: "star.fill") - public var pushNotificationTitle: String = "내가 여기있는데 어디갔냐옹!" +public enum CatType: String, CaseIterable, Identifiable { + public var id: String { self.rawValue } + case cheese, black, calico } -public struct CatFactory { - public static func makeCat(type: CatType) -> any CatFactoryProtocol { - switch type { +extension CatType: CatFactoryProtocol { + public var keyword: LocalizedStringKey { + switch self { + case .cheese: + "응원" + case .black: + "긍정" + case .calico: + "자극" + } + } + + public var keywordImage: Image { + switch self { + case .cheese: + DesignSystemAsset.Image._16Star.swiftUIImage + case .black: + DesignSystemAsset.Image._16Heart.swiftUIImage + case .calico: + DesignSystemAsset.Image._16Focus.swiftUIImage + } + } + + public var typeName: LocalizedStringKey { + switch self { + case .cheese: + "치즈냥" + case .black: + "까만냥" + case .calico: + "삼색냥" + } + } + + public var catImage: Image { + switch self { + case .cheese: + Image(systemName: "star.fill") + case .black: + Image(systemName: "star.fill") + case .calico: + Image(systemName: "star.fill") + } + } + + public var pushNotificationTitle: String { + switch self { case .cheese: - return CheeseCat() + "어디갔냐옹..." case .black: - return BlackCat() + "어디갔냐옹..." case .calico: - return CalicoCat() + "내가 여기있는데 어디갔냐옹!" } } } diff --git a/Projects/Feature/OnboardingFeature/Sources/SelectCat/SelectCatCore.swift b/Projects/Feature/OnboardingFeature/Sources/SelectCat/SelectCatCore.swift index b19bdd3..d21e515 100644 --- a/Projects/Feature/OnboardingFeature/Sources/SelectCat/SelectCatCore.swift +++ b/Projects/Feature/OnboardingFeature/Sources/SelectCat/SelectCatCore.swift @@ -6,29 +6,30 @@ // Copyright © 2024 PomoNyang. All rights reserved. // +import DatabaseClientInterface + import ComposableArchitecture @Reducer public struct SelectCatCore { @ObservableState public struct State: Equatable { - var catType: CatType? - var cat: (any CatFactoryProtocol)? { - if let catType = catType { - return CatFactory.makeCat(type: catType) - } else { - return nil - } - } + var catType: CatType? = nil } - public enum Action { + public enum Action: BindableAction { case onAppear + case selectCat(CatType) + case tapNextButton + case binding(BindingAction) } public init() {} - + + @Dependency(DatabaseClient.self) var databaseClient + public var body: some ReducerOf { + BindingReducer() Reduce(self.core) } @@ -36,6 +37,13 @@ public struct SelectCatCore { switch action { case .onAppear: return .none + case.selectCat(let catType): + state.catType = (state.catType == catType) ? nil : catType + return .none + case .tapNextButton: + return .none + case .binding: + return .none } } } diff --git a/Projects/Feature/OnboardingFeature/Sources/SelectCat/SelectCatView.swift b/Projects/Feature/OnboardingFeature/Sources/SelectCat/SelectCatView.swift index c80cb78..6a843ea 100644 --- a/Projects/Feature/OnboardingFeature/Sources/SelectCat/SelectCatView.swift +++ b/Projects/Feature/OnboardingFeature/Sources/SelectCat/SelectCatView.swift @@ -25,27 +25,53 @@ public struct SelectCatView: View { style: .navigation, navBackground: .clear ) { - VStack { - VStack(spacing: Alias.Spacing.xSmall) { - Text("어떤 고양이와 함께할까요?") - .font(Typography.header3) - .foregroundStyle(Alias.Color.Text.primary) - Text("언제든지 다른 고양이를 선택할 수 있어요") - .font(Typography.bodyR) - .foregroundStyle(Alias.Color.Text.secondary) + VStack(spacing: 0) { + HStack { + VStack(spacing: Alias.Spacing.xSmall) { + Text("어떤 고양이와 함께할까요?") + .font(Typography.header3) + .foregroundStyle(Alias.Color.Text.primary) + Text("언제든지 다른 고양이를 선택할 수 있어요") + .font(Typography.bodyR) + .foregroundStyle(Alias.Color.Text.secondary) + } + Spacer() } + .padding(.top, Alias.Spacing.xLarge) Spacer(minLength: Alias.Spacing.xLarge) - VStack { - CatPushNotificationExampleView(cat: store.cat) + VStack(spacing: Alias.Spacing.small) { + CatPushNotificationExampleView(catType: $store.catType) + ZStack { + Rectangle() + .foregroundStyle(Alias.Color.Background.secondary) + .frame(height: 240) + store.catType?.catImage + } + + HStack { + ForEach(CatType.allCases) { catType in + Button( + title: catType.typeName, + subtitle: catType.keyword, + rightIcon: catType.keywordImage, + action: { store.send(.selectCat(catType)) } + ) + .buttonStyle(.select(isSelected: store.catType == catType)) + } + } + .padding(.top, 34) } + Spacer(minLength: Alias.Spacing.large) + Button(title: "이 고양이와 함께하기") { - // click! + store.send(.tapNextButton) } - .buttonStyle(.box(level: .primary, size: .large, width: .medium)) + .buttonStyle(.box(level: .primary, size: .large, width: .low)) } + .padding(.horizontal, 20) } .background { Alias.Color.Background.primary @@ -55,39 +81,44 @@ public struct SelectCatView: View { } struct CatPushNotificationExampleView: View { - var cat: (any CatFactoryProtocol)? + @Binding var catType: CatType? + var body: some View { ZStack { RoundedRectangle(cornerRadius: Alias.BorderRadius.xSmall,style: .circular) .foregroundStyle(Alias.Color.Background.secondary) - if let cat = cat { - HStack { + if let catType = catType { + HStack(spacing: 10){ Image(systemName: "star.fill") VStack(spacing: 0) { - Text("모하냥") - .font(Typography.bodyR) - .foregroundStyle(Alias.Color.Text.primary) - Text(cat.pushNotificationTitle) - .font(Typography.subBodyR) - .foregroundStyle(Alias.Color.Text.primary) + HStack { + Text("모하냥") + .font(Typography.bodyR) + .foregroundStyle(Alias.Color.Text.primary) + Spacer() + Text("지금") + .font(Typography.subBodyR) + .foregroundStyle(Alias.Color.Text.secondary) + } + HStack { + Text(catType.pushNotificationTitle) + .font(Typography.subBodyR) + .foregroundStyle(Alias.Color.Text.primary) + Spacer() + } } Spacer() - VStack(spacing: 0) { - Text("지금") - .font(Typography.subBodyR) - .foregroundStyle(Alias.Color.Text.secondary) - Spacer() - } } - .padding(.all, Alias.Spacing.large) + .padding(.all, Alias.Spacing.medium) } else { Text("고양이를 선택하면\n딴 짓 방해알림 예시를 보여드려요") .font(Typography.bodyR) .foregroundStyle(Alias.Color.Text.tertiary) .multilineTextAlignment(.center) - .padding(.all, Alias.Spacing.large) + .padding(.all, Alias.Spacing.medium) } } + .frame(height: 72) } } From 13360560a39bfa5f872f5ef65ccab191c305d0a2 Mon Sep 17 00:00:00 2001 From: Jihyun247 Date: Fri, 16 Aug 2024 02:29:52 +0900 Subject: [PATCH 09/14] feature: userService GET /cats --- .../Module/Domain.swift | 1 + .../Core/KeychainClient/Interface/Keys.swift | 15 ------- .../Sources/API/AuthAPIClient.swift | 10 +++-- Projects/Domain/Domain/Sources/Exports.swift | 2 + .../API/UserAPIClientInterface.swift | 27 ++++++++++++ .../Interface/API/UserAPIRequest.swift | 41 +++++++++++++++++++ .../UserService/Interface/DTO/UserDTO.swift | 27 ++++++++++++ Projects/Domain/UserService/Project.swift | 20 +++++++++ .../Sources/API/UserAPIClient.swift | 30 ++++++++++++++ .../Sources/SelectCat/SelectCatCore.swift | 19 +++++++-- .../Sources/SelectCat/SelectCatView.swift | 13 +++--- .../SplashFeature/Sources/SplashCore.swift | 4 +- 12 files changed, 180 insertions(+), 29 deletions(-) delete mode 100644 Projects/Core/KeychainClient/Interface/Keys.swift create mode 100644 Projects/Domain/UserService/Interface/API/UserAPIClientInterface.swift create mode 100644 Projects/Domain/UserService/Interface/API/UserAPIRequest.swift create mode 100644 Projects/Domain/UserService/Interface/DTO/UserDTO.swift create mode 100644 Projects/Domain/UserService/Project.swift create mode 100644 Projects/Domain/UserService/Sources/API/UserAPIClient.swift diff --git a/Plugins/DependencyPlugin/ProjectDescriptionHelpers/Module/Domain.swift b/Plugins/DependencyPlugin/ProjectDescriptionHelpers/Module/Domain.swift index 4b17516..0f5f3df 100644 --- a/Plugins/DependencyPlugin/ProjectDescriptionHelpers/Module/Domain.swift +++ b/Plugins/DependencyPlugin/ProjectDescriptionHelpers/Module/Domain.swift @@ -12,4 +12,5 @@ public enum Domain: String, Modulable { case AppService case AuthService case PushService + case UserService } diff --git a/Projects/Core/KeychainClient/Interface/Keys.swift b/Projects/Core/KeychainClient/Interface/Keys.swift deleted file mode 100644 index 5d00ab7..0000000 --- a/Projects/Core/KeychainClient/Interface/Keys.swift +++ /dev/null @@ -1,15 +0,0 @@ -// -// Keys.swift -// KeychainClientInterface -// -// Created by 김지현 on 8/9/24. -// Copyright © 2024 PomoNyang. All rights reserved. -// - -import Foundation - -public enum KeychainKeys: String { - case accessToken = "mohanyang_keychain_access_token" - case refreshToken = "mohanyang_keychain_refresh_token" - case deviceID = "mohanyang_keychain_deviceID" -} diff --git a/Projects/Domain/AuthService/Sources/API/AuthAPIClient.swift b/Projects/Domain/AuthService/Sources/API/AuthAPIClient.swift index dafa8fa..cead1cd 100644 --- a/Projects/Domain/AuthService/Sources/API/AuthAPIClient.swift +++ b/Projects/Domain/AuthService/Sources/API/AuthAPIClient.swift @@ -17,20 +17,24 @@ extension AuthService: DependencyKey { private static func live() -> Self { return AuthService( login: { deviceID, apiClient, keychainClient in - guard isTokenValid(keychainClient) else { return } + guard !isTokenValid(keychainClient) else { return } + let service = AuthAPIRequest.login(deviceID) let response = try await apiClient.apiRequest( request: service, as: AuthDTO.Response.TokenResponseDTO.self, isWithInterceptor: false ) - _ = keychainClient.create(key: KeychainKeys.accessToken.rawValue, data: response.accessToken) + + _ = keychainClient.create(key: "mohanyang_keychain_access_token", data: response.accessToken) + _ = keychainClient.create(key: "mohanyang_keychain_refresh_token", data: response.refreshToken) return } ) } private static func isTokenValid(_ keychainClient: KeychainClient) -> Bool { - return keychainClient.read(key: KeychainKeys.accessToken.rawValue) != nil + let isTokenExist = keychainClient.read(key: "mohanyang_keychain_access_token") + return isTokenExist != nil } } diff --git a/Projects/Domain/Domain/Sources/Exports.swift b/Projects/Domain/Domain/Sources/Exports.swift index 9059185..c92564b 100644 --- a/Projects/Domain/Domain/Sources/Exports.swift +++ b/Projects/Domain/Domain/Sources/Exports.swift @@ -10,3 +10,5 @@ @_exported import PushService @_exported import AuthService @_exported import AuthServiceInterface +@_exported import UserService +@_exported import UserServiceInterface diff --git a/Projects/Domain/UserService/Interface/API/UserAPIClientInterface.swift b/Projects/Domain/UserService/Interface/API/UserAPIClientInterface.swift new file mode 100644 index 0000000..029f9e1 --- /dev/null +++ b/Projects/Domain/UserService/Interface/API/UserAPIClientInterface.swift @@ -0,0 +1,27 @@ +// +// UserAPIClientInterface.swift +// UserService +// +// Created by 김지현 on 8/16/24. +// Copyright © 2024 PomoNyang. All rights reserved. +// + +import Foundation + +import APIClientInterface + +import Dependencies +import DependenciesMacros + + +@DependencyClient +public struct UserService { + public var getCatLists: @Sendable ( + _ apiClient: APIClient + ) async throws -> CatList +} + +extension UserService: TestDependencyKey { + public static let previewValue = Self() + public static let testValue = Self() +} diff --git a/Projects/Domain/UserService/Interface/API/UserAPIRequest.swift b/Projects/Domain/UserService/Interface/API/UserAPIRequest.swift new file mode 100644 index 0000000..153e105 --- /dev/null +++ b/Projects/Domain/UserService/Interface/API/UserAPIRequest.swift @@ -0,0 +1,41 @@ +// +// UserAPIRequest.swift +// UserService +// +// Created by 김지현 on 8/16/24. +// Copyright © 2024 PomoNyang. All rights reserved. +// + +import Foundation +import APIClientInterface + +public enum UserAPIrequest { + case getCatList +} + +extension UserAPIrequest: APIBaseRequest { + public var baseURL: String { + return API.apiBaseURL + } + + public var path: String { + switch self { + case .getCatList: + return "/api/v1/cats" + } + } + + public var method: HTTPMethod { + switch self { + case .getCatList: + return .get + } + } + + public var parameters: RequestParams { + switch self { + case .getCatList: + return .requestPlain + } + } +} diff --git a/Projects/Domain/UserService/Interface/DTO/UserDTO.swift b/Projects/Domain/UserService/Interface/DTO/UserDTO.swift new file mode 100644 index 0000000..fe4beea --- /dev/null +++ b/Projects/Domain/UserService/Interface/DTO/UserDTO.swift @@ -0,0 +1,27 @@ +// +// UserDTO.swift +// UserService +// +// Created by 김지현 on 8/16/24. +// Copyright © 2024 PomoNyang. All rights reserved. +// + +import Foundation + +public typealias CatList = [UserDTO.Response.GetCatListResponseDTO] + +public enum UserDTO { + public enum Request { } + public enum Response { } +} + +public extension UserDTO.Request { +} + +public extension UserDTO.Response { + struct GetCatListResponseDTO: Equatable, Decodable { + public var no: Int + public var name: String + public var type: String + } +} diff --git a/Projects/Domain/UserService/Project.swift b/Projects/Domain/UserService/Project.swift new file mode 100644 index 0000000..259b532 --- /dev/null +++ b/Projects/Domain/UserService/Project.swift @@ -0,0 +1,20 @@ +import ProjectDescription +import ProjectDescriptionHelpers + +@_spi(Domain) +@_spi(Core) +import DependencyPlugin + +let project: Project = .makeTMABasedProject( + module: Domain.UserService, + scripts: [], + targets: [ + .sources, + .interface + ], + dependencies: [ + .interface: [ + .dependency(rootModule: Core.self) + ] + ] +) diff --git a/Projects/Domain/UserService/Sources/API/UserAPIClient.swift b/Projects/Domain/UserService/Sources/API/UserAPIClient.swift new file mode 100644 index 0000000..f95b66f --- /dev/null +++ b/Projects/Domain/UserService/Sources/API/UserAPIClient.swift @@ -0,0 +1,30 @@ +// +// UserAPIClient.swift +// UserService +// +// Created by 김지현 on 8/16/24. +// Copyright © 2024 PomoNyang. All rights reserved. +// + +import Foundation + +import APIClientInterface +import UserServiceInterface + +import Dependencies + +extension UserService: DependencyKey { + public static let liveValue: UserService = .live() + private static func live() -> Self { + return UserService( + getCatLists: { apiClient in + let request = UserAPIrequest.getCatList + return try await apiClient.apiRequest( + request: request, + as: CatList.self, + isWithInterceptor: true + ) + } + ) + } +} diff --git a/Projects/Feature/OnboardingFeature/Sources/SelectCat/SelectCatCore.swift b/Projects/Feature/OnboardingFeature/Sources/SelectCat/SelectCatCore.swift index d21e515..e9b49c3 100644 --- a/Projects/Feature/OnboardingFeature/Sources/SelectCat/SelectCatCore.swift +++ b/Projects/Feature/OnboardingFeature/Sources/SelectCat/SelectCatCore.swift @@ -6,7 +6,8 @@ // Copyright © 2024 PomoNyang. All rights reserved. // -import DatabaseClientInterface +import APIClientInterface +import UserServiceInterface import ComposableArchitecture @@ -14,6 +15,7 @@ import ComposableArchitecture public struct SelectCatCore { @ObservableState public struct State: Equatable { + var catList: CatList = [] var catType: CatType? = nil } @@ -21,12 +23,15 @@ public struct SelectCatCore { case onAppear case selectCat(CatType) case tapNextButton + case _fetchCatListRequest + case _fetchCatListResponse(CatList) case binding(BindingAction) } public init() {} - @Dependency(DatabaseClient.self) var databaseClient + @Dependency(APIClient.self) var apiClient + @Dependency(UserService.self) var userService public var body: some ReducerOf { BindingReducer() @@ -36,12 +41,20 @@ public struct SelectCatCore { private func core(state: inout State, action: Action) -> EffectOf { switch action { case .onAppear: - return .none + return .run { send in await send(._fetchCatListRequest) } case.selectCat(let catType): state.catType = (state.catType == catType) ? nil : catType return .none case .tapNextButton: return .none + case ._fetchCatListRequest: + return .run { send in + let response = try await userService.getCatLists(apiClient: apiClient) + await send(._fetchCatListResponse(response)) + } + case ._fetchCatListResponse(let catList): + state.catList = catList + return .none case .binding: return .none } diff --git a/Projects/Feature/OnboardingFeature/Sources/SelectCat/SelectCatView.swift b/Projects/Feature/OnboardingFeature/Sources/SelectCat/SelectCatView.swift index 6a843ea..6d8354d 100644 --- a/Projects/Feature/OnboardingFeature/Sources/SelectCat/SelectCatView.swift +++ b/Projects/Feature/OnboardingFeature/Sources/SelectCat/SelectCatView.swift @@ -51,14 +51,14 @@ public struct SelectCatView: View { } HStack { - ForEach(CatType.allCases) { catType in + ForEach(store.catList, id: \.no) { cat in Button( - title: catType.typeName, - subtitle: catType.keyword, - rightIcon: catType.keywordImage, - action: { store.send(.selectCat(catType)) } + title: LocalizedStringKey(cat.name), + subtitle: LocalizedStringKey(cat.name), + rightIcon: DesignSystemAsset.Image._16Star.swiftUIImage, + action: { }//store.send(.selectCat(catType)) } ) - .buttonStyle(.select(isSelected: store.catType == catType)) + .buttonStyle(.select(isSelected: false)) } } .padding(.top, 34) @@ -73,6 +73,7 @@ public struct SelectCatView: View { } .padding(.horizontal, 20) } + .onAppear { store.send(.onAppear) } .background { Alias.Color.Background.primary .ignoresSafeArea() diff --git a/Projects/Feature/SplashFeature/Sources/SplashCore.swift b/Projects/Feature/SplashFeature/Sources/SplashCore.swift index 48a023b..3811e5e 100644 --- a/Projects/Feature/SplashFeature/Sources/SplashCore.swift +++ b/Projects/Feature/SplashFeature/Sources/SplashCore.swift @@ -60,7 +60,7 @@ public struct SplashCore { extension SplashCore { private func checkDeviceIDExist() -> Effect { - let deviceID = keychainClient.read(key: KeychainKeys.deviceID.rawValue) ?? getDeviceUUID() + let deviceID = keychainClient.read(key: "mohanyang_keychain_device_id") ?? getDeviceUUID() return login(deviceID: deviceID) } @@ -81,7 +81,7 @@ extension SplashCore { private func getDeviceUUID() -> String { guard let uuid = UIDevice.current.identifierForVendor?.uuidString, - keychainClient.create(key: KeychainKeys.deviceID.rawValue, data: uuid) else { + keychainClient.create(key: "mohanyang_keychain_device_id", data: uuid) else { return "" } return uuid From 2cc7a7fc71c3a427ba13f7b61ee8e19a00bd6171 Mon Sep 17 00:00:00 2001 From: Jihyun247 Date: Fri, 16 Aug 2024 03:28:26 +0900 Subject: [PATCH 10/14] feature: userService PUT users/cats --- .../Interface/APIClientInterface.swift | 9 ++++++- .../API/UserAPIClientInterface.swift | 6 ++++- .../Interface/API/UserAPIRequest.swift | 15 ++++++++--- .../UserService/Interface/DTO/UserDTO.swift | 5 ++++ .../Sources/API/UserAPIClient.swift | 15 ++++++++--- .../Sources/SelectCat/CatFactory.swift | 1 - .../Sources/SelectCat/SelectCatCore.swift | 27 ++++++++++++++----- .../Sources/SelectCat/SelectCatView.swift | 15 ++++++----- 8 files changed, 69 insertions(+), 24 deletions(-) diff --git a/Projects/Core/APIClient/Interface/APIClientInterface.swift b/Projects/Core/APIClient/Interface/APIClientInterface.swift index d9fa7cc..c6bbb46 100644 --- a/Projects/Core/APIClient/Interface/APIClientInterface.swift +++ b/Projects/Core/APIClient/Interface/APIClientInterface.swift @@ -22,11 +22,15 @@ public struct APIClient { public func apiRequest( request: APIBaseRequest, - as: T.Type, + as: T.Type = T.self, isWithInterceptor: Bool = true ) async throws -> T { let (data, _) = try await self.apiRequest(request, isWithInterceptor) + if T.self == EmptyResponse.self { + return EmptyResponse() as! T + } + do { let decodedData = try JSONDecoder().decode(T.self, from: data) return decodedData @@ -40,3 +44,6 @@ extension APIClient: TestDependencyKey { public static let previewValue = Self() public static let testValue = Self() } + +// MARK: Empty Response 대응 논의 필요 +public struct EmptyResponse: Decodable {} diff --git a/Projects/Domain/UserService/Interface/API/UserAPIClientInterface.swift b/Projects/Domain/UserService/Interface/API/UserAPIClientInterface.swift index 029f9e1..b65637d 100644 --- a/Projects/Domain/UserService/Interface/API/UserAPIClientInterface.swift +++ b/Projects/Domain/UserService/Interface/API/UserAPIClientInterface.swift @@ -16,9 +16,13 @@ import DependenciesMacros @DependencyClient public struct UserService { - public var getCatLists: @Sendable ( + public var fetchCatLists: @Sendable ( _ apiClient: APIClient ) async throws -> CatList + public var selectCat: @Sendable ( + _ no: Int, + _ apiClient: APIClient + ) async throws -> Void } extension UserService: TestDependencyKey { diff --git a/Projects/Domain/UserService/Interface/API/UserAPIRequest.swift b/Projects/Domain/UserService/Interface/API/UserAPIRequest.swift index 153e105..babbc87 100644 --- a/Projects/Domain/UserService/Interface/API/UserAPIRequest.swift +++ b/Projects/Domain/UserService/Interface/API/UserAPIRequest.swift @@ -10,7 +10,7 @@ import Foundation import APIClientInterface public enum UserAPIrequest { - case getCatList + case fetchCatList, selectCat(Int) } extension UserAPIrequest: APIBaseRequest { @@ -20,22 +20,29 @@ extension UserAPIrequest: APIBaseRequest { public var path: String { switch self { - case .getCatList: + case .fetchCatList: return "/api/v1/cats" + case .selectCat: + return "/api/v1/users/cats" } } public var method: HTTPMethod { switch self { - case .getCatList: + case .fetchCatList: return .get + case .selectCat: + return .put } } public var parameters: RequestParams { switch self { - case .getCatList: + case .fetchCatList: return .requestPlain + case .selectCat(let no): + let dto = UserDTO.Request.SelectCatRequestDTO(catNo: no) + return .body(dto) } } } diff --git a/Projects/Domain/UserService/Interface/DTO/UserDTO.swift b/Projects/Domain/UserService/Interface/DTO/UserDTO.swift index fe4beea..24f86b2 100644 --- a/Projects/Domain/UserService/Interface/DTO/UserDTO.swift +++ b/Projects/Domain/UserService/Interface/DTO/UserDTO.swift @@ -16,6 +16,9 @@ public enum UserDTO { } public extension UserDTO.Request { + struct SelectCatRequestDTO: Encodable { + public var catNo: Int + } } public extension UserDTO.Response { @@ -24,4 +27,6 @@ public extension UserDTO.Response { public var name: String public var type: String } + + struct SelectCatResponseDTO: Decodable { } } diff --git a/Projects/Domain/UserService/Sources/API/UserAPIClient.swift b/Projects/Domain/UserService/Sources/API/UserAPIClient.swift index f95b66f..91077c3 100644 --- a/Projects/Domain/UserService/Sources/API/UserAPIClient.swift +++ b/Projects/Domain/UserService/Sources/API/UserAPIClient.swift @@ -17,13 +17,20 @@ extension UserService: DependencyKey { public static let liveValue: UserService = .live() private static func live() -> Self { return UserService( - getCatLists: { apiClient in - let request = UserAPIrequest.getCatList + fetchCatLists: { apiClient in + let request = UserAPIrequest.fetchCatList return try await apiClient.apiRequest( request: request, - as: CatList.self, - isWithInterceptor: true + as: CatList.self ) + }, + selectCat: { no, apiClient in + let request = UserAPIrequest.selectCat(no) + _ = try await apiClient.apiRequest( + request: request, + as: EmptyResponse.self + ) + return } ) } diff --git a/Projects/Feature/OnboardingFeature/Sources/SelectCat/CatFactory.swift b/Projects/Feature/OnboardingFeature/Sources/SelectCat/CatFactory.swift index fb3869c..7fa0173 100644 --- a/Projects/Feature/OnboardingFeature/Sources/SelectCat/CatFactory.swift +++ b/Projects/Feature/OnboardingFeature/Sources/SelectCat/CatFactory.swift @@ -7,7 +7,6 @@ // import SwiftUI - import DesignSystem public protocol CatFactoryProtocol { diff --git a/Projects/Feature/OnboardingFeature/Sources/SelectCat/SelectCatCore.swift b/Projects/Feature/OnboardingFeature/Sources/SelectCat/SelectCatCore.swift index e9b49c3..20feb4f 100644 --- a/Projects/Feature/OnboardingFeature/Sources/SelectCat/SelectCatCore.swift +++ b/Projects/Feature/OnboardingFeature/Sources/SelectCat/SelectCatCore.swift @@ -16,15 +16,17 @@ public struct SelectCatCore { @ObservableState public struct State: Equatable { var catList: CatList = [] - var catType: CatType? = nil + //var catType: CatType? = nil + var selectedCat: Int? = nil } public enum Action: BindableAction { case onAppear - case selectCat(CatType) + case selectCat(Int) case tapNextButton case _fetchCatListRequest case _fetchCatListResponse(CatList) + case _selectCatRequest case binding(BindingAction) } @@ -42,19 +44,32 @@ public struct SelectCatCore { switch action { case .onAppear: return .run { send in await send(._fetchCatListRequest) } - case.selectCat(let catType): - state.catType = (state.catType == catType) ? nil : catType + + case.selectCat(let selectedCat): + state.selectedCat = (state.selectedCat == selectedCat) ? nil : selectedCat return .none + case .tapNextButton: - return .none + return .run { send in await send(._selectCatRequest) } + case ._fetchCatListRequest: return .run { send in - let response = try await userService.getCatLists(apiClient: apiClient) + let response = try await userService.fetchCatLists(apiClient: apiClient) await send(._fetchCatListResponse(response)) } + case ._fetchCatListResponse(let catList): state.catList = catList return .none + + case ._selectCatRequest: + guard let selectedCat = state.selectedCat else { return .none } + return .run { send in + _ = try await userService.selectCat(no: selectedCat, apiClient: apiClient) + // go to naming cat + print("성공 !!!!! \(selectedCat)") + } + case .binding: return .none } diff --git a/Projects/Feature/OnboardingFeature/Sources/SelectCat/SelectCatView.swift b/Projects/Feature/OnboardingFeature/Sources/SelectCat/SelectCatView.swift index 6d8354d..9ebf0dd 100644 --- a/Projects/Feature/OnboardingFeature/Sources/SelectCat/SelectCatView.swift +++ b/Projects/Feature/OnboardingFeature/Sources/SelectCat/SelectCatView.swift @@ -42,12 +42,12 @@ public struct SelectCatView: View { Spacer(minLength: Alias.Spacing.xLarge) VStack(spacing: Alias.Spacing.small) { - CatPushNotificationExampleView(catType: $store.catType) + CatPushNotificationExampleView(selectedCat: $store.selectedCat) ZStack { Rectangle() .foregroundStyle(Alias.Color.Background.secondary) .frame(height: 240) - store.catType?.catImage + // Cat Image } HStack { @@ -56,9 +56,9 @@ public struct SelectCatView: View { title: LocalizedStringKey(cat.name), subtitle: LocalizedStringKey(cat.name), rightIcon: DesignSystemAsset.Image._16Star.swiftUIImage, - action: { }//store.send(.selectCat(catType)) } + action: { store.send(.selectCat(cat.no)) } ) - .buttonStyle(.select(isSelected: false)) + .buttonStyle(.select(isSelected: cat.no == store.selectedCat)) } } .padding(.top, 34) @@ -70,6 +70,7 @@ public struct SelectCatView: View { store.send(.tapNextButton) } .buttonStyle(.box(level: .primary, size: .large, width: .low)) + .disabled(store.selectedCat == nil) } .padding(.horizontal, 20) } @@ -82,14 +83,14 @@ public struct SelectCatView: View { } struct CatPushNotificationExampleView: View { - @Binding var catType: CatType? + @Binding var selectedCat: Int? var body: some View { ZStack { RoundedRectangle(cornerRadius: Alias.BorderRadius.xSmall,style: .circular) .foregroundStyle(Alias.Color.Background.secondary) - if let catType = catType { + if let selectedCat = selectedCat { HStack(spacing: 10){ Image(systemName: "star.fill") VStack(spacing: 0) { @@ -103,7 +104,7 @@ struct CatPushNotificationExampleView: View { .foregroundStyle(Alias.Color.Text.secondary) } HStack { - Text(catType.pushNotificationTitle) + Text("흐으으음") .font(Typography.subBodyR) .foregroundStyle(Alias.Color.Text.primary) Spacer() From 39802377f8a114b3732b80688e863afb283fe2a1 Mon Sep 17 00:00:00 2001 From: Jihyun247 Date: Fri, 16 Aug 2024 20:21:48 +0900 Subject: [PATCH 11/14] =?UTF-8?q?feature:=20=EA=B3=A0=EC=96=91=EC=9D=B4=20?= =?UTF-8?q?=EA=B5=AC=EC=A1=B0=EC=B2=B4=20=EB=8B=A4=EC=8B=9C=20=EC=A0=95?= =?UTF-8?q?=EC=9D=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Sources/SelectCat/CatFactory.swift | 138 +++++++++++------- .../Sources/SelectCat/SelectCatCore.swift | 20 ++- .../Sources/SelectCat/SelectCatView.swift | 16 +- .../Utils/Sources/Extensions/String+.swift | 22 +++ 4 files changed, 127 insertions(+), 69 deletions(-) create mode 100644 Projects/Shared/Utils/Sources/Extensions/String+.swift diff --git a/Projects/Feature/OnboardingFeature/Sources/SelectCat/CatFactory.swift b/Projects/Feature/OnboardingFeature/Sources/SelectCat/CatFactory.swift index 7fa0173..5f42ca2 100644 --- a/Projects/Feature/OnboardingFeature/Sources/SelectCat/CatFactory.swift +++ b/Projects/Feature/OnboardingFeature/Sources/SelectCat/CatFactory.swift @@ -9,72 +9,100 @@ import SwiftUI import DesignSystem +public enum CatType: String, Equatable { + case cheese, black, threeColor +} + public protocol CatFactoryProtocol { - var keyword: LocalizedStringKey { get } - var keywordImage: Image { get } - var typeName: LocalizedStringKey { get } + var id: String { get } // catType의 rawValue + var no: Int { get } // 서버에서 주는 no + var name: String { get } // 서버에서 주는 이름 + var keyword: String { get } + var keywordImage: Image { get } // 키워드에 따른 아이콘 이미지 var catImage: Image { get } - var pushNotificationTitle: String { get } + var pushNotificationTitle: String { get } // 푸시알림 예시 글귀 } -public enum CatType: String, CaseIterable, Identifiable { - public var id: String { self.rawValue } - case cheese, black, calico -} +// MARK: ANY CAT +public struct AnyCat: CatFactoryProtocol, Identifiable, Equatable { + private let base: CatFactoryProtocol + public init(_ base: CatFactoryProtocol) { + self.base = base + } -extension CatType: CatFactoryProtocol { - public var keyword: LocalizedStringKey { - switch self { - case .cheese: - "응원" - case .black: - "긍정" - case .calico: - "자극" - } + public var id: String { base.id } + public var no: Int { base.no } + public var keyword: String { base.keyword } + public var keywordImage: Image { base.keywordImage } + public var name: String { base.name } + public var catImage: Image { base.catImage } + public var pushNotificationTitle: String { base.pushNotificationTitle } + + public static func == (lhs: AnyCat, rhs: AnyCat) -> Bool { + lhs.base.id == rhs.base.id } - - public var keywordImage: Image { - switch self { - case .cheese: - DesignSystemAsset.Image._16Star.swiftUIImage - case .black: - DesignSystemAsset.Image._16Heart.swiftUIImage - case .calico: - DesignSystemAsset.Image._16Focus.swiftUIImage - } +} + +// MARK: CHEESE CAT +public struct CheeseCat: CatFactoryProtocol { + public init(no: Int, name: String) { + self.no = no + self.name = name } - - public var typeName: LocalizedStringKey { - switch self { - case .cheese: - "치즈냥" - case .black: - "까만냥" - case .calico: - "삼색냥" - } + + public var id: String = "CHEESE" + public var no: Int + public var name: String + public var keyword: String = "응원" + public var keywordImage: Image = DesignSystemAsset.Image._16Star.swiftUIImage + public var catImage: Image = Image(systemName: "star.fill") + public var pushNotificationTitle: String = "어디갔냐옹..." +} + +// MARK: BLACK CAT +public struct BlackCat: CatFactoryProtocol { + public init(no: Int, name: String) { + self.no = no + self.name = name } - - public var catImage: Image { - switch self { - case .cheese: - Image(systemName: "star.fill") - case .black: - Image(systemName: "star.fill") - case .calico: - Image(systemName: "star.fill") - } + + public var id: String = "BLACK" + public var no: Int + public var name: String + public var keyword: String = "긍정" + public var keywordImage: Image = DesignSystemAsset.Image._16Heart.swiftUIImage + public var catImage: Image = Image(systemName: "star") + public var pushNotificationTitle: String = "어디갔냐옹..." +} + +// MARK: THREE_COLOR CAT +public struct ThreeColorCat: CatFactoryProtocol { + public init(no: Int, name: String) { + self.no = no + self.name = name } - - public var pushNotificationTitle: String { - switch self { + + public var id: String = "THREE_COLOR" + public var no: Int + public var name: String + public var keyword: String = "자극" + public var keywordImage: Image = DesignSystemAsset.Image._16Focus.swiftUIImage + public var catImage: Image = Image(systemName: "star.fill") + public var pushNotificationTitle: String = "내가 여기있는데 어디갔냐옹!" +} + +// MARK: MAKE CAT +public struct CatFactory { + public static func makeCat(type: CatType, no: Int, name: String) -> AnyCat { + let cat: CatFactoryProtocol + switch type { case .cheese: - "어디갔냐옹..." + cat = CheeseCat(no: no, name: name) case .black: - "어디갔냐옹..." - case .calico: - "내가 여기있는데 어디갔냐옹!" + cat = BlackCat(no: no, name: name) + case .threeColor: + cat = ThreeColorCat(no: no, name: name) } + return AnyCat(cat) } } diff --git a/Projects/Feature/OnboardingFeature/Sources/SelectCat/SelectCatCore.swift b/Projects/Feature/OnboardingFeature/Sources/SelectCat/SelectCatCore.swift index 20feb4f..94b8fb1 100644 --- a/Projects/Feature/OnboardingFeature/Sources/SelectCat/SelectCatCore.swift +++ b/Projects/Feature/OnboardingFeature/Sources/SelectCat/SelectCatCore.swift @@ -8,6 +8,7 @@ import APIClientInterface import UserServiceInterface +import Shared import ComposableArchitecture @@ -15,14 +16,14 @@ import ComposableArchitecture public struct SelectCatCore { @ObservableState public struct State: Equatable { - var catList: CatList = [] + var catList: [AnyCat] = [] //var catType: CatType? = nil - var selectedCat: Int? = nil + var selectedCat: AnyCat? = nil } public enum Action: BindableAction { case onAppear - case selectCat(Int) + case selectCat(AnyCat) case tapNextButton case _fetchCatListRequest case _fetchCatListResponse(CatList) @@ -59,15 +60,22 @@ public struct SelectCatCore { } case ._fetchCatListResponse(let catList): - state.catList = catList + print(CatType.cheese.rawValue, CatType.black.rawValue, CatType.threeColor.rawValue) + state.catList = catList.map { cat in + CatFactory.makeCat( + type: CatType(rawValue: cat.type.camelCased()) ?? .cheese, + no: cat.no, + name: cat.name + ) + } return .none case ._selectCatRequest: guard let selectedCat = state.selectedCat else { return .none } return .run { send in - _ = try await userService.selectCat(no: selectedCat, apiClient: apiClient) + _ = try await userService.selectCat(no: selectedCat.no, apiClient: apiClient) + // user notification 요청 // go to naming cat - print("성공 !!!!! \(selectedCat)") } case .binding: diff --git a/Projects/Feature/OnboardingFeature/Sources/SelectCat/SelectCatView.swift b/Projects/Feature/OnboardingFeature/Sources/SelectCat/SelectCatView.swift index 9ebf0dd..3b3b99e 100644 --- a/Projects/Feature/OnboardingFeature/Sources/SelectCat/SelectCatView.swift +++ b/Projects/Feature/OnboardingFeature/Sources/SelectCat/SelectCatView.swift @@ -47,18 +47,18 @@ public struct SelectCatView: View { Rectangle() .foregroundStyle(Alias.Color.Background.secondary) .frame(height: 240) - // Cat Image + store.selectedCat?.catImage } HStack { - ForEach(store.catList, id: \.no) { cat in + ForEach(store.catList) { cat in Button( title: LocalizedStringKey(cat.name), - subtitle: LocalizedStringKey(cat.name), - rightIcon: DesignSystemAsset.Image._16Star.swiftUIImage, - action: { store.send(.selectCat(cat.no)) } + subtitle: LocalizedStringKey(cat.keyword), + rightIcon: cat.keywordImage, + action: { store.send(.selectCat(cat)) } ) - .buttonStyle(.select(isSelected: cat.no == store.selectedCat)) + .buttonStyle(.select(isSelected: cat == store.selectedCat)) } } .padding(.top, 34) @@ -83,7 +83,7 @@ public struct SelectCatView: View { } struct CatPushNotificationExampleView: View { - @Binding var selectedCat: Int? + @Binding var selectedCat: AnyCat? var body: some View { ZStack { @@ -104,7 +104,7 @@ struct CatPushNotificationExampleView: View { .foregroundStyle(Alias.Color.Text.secondary) } HStack { - Text("흐으으음") + Text(selectedCat.pushNotificationTitle) .font(Typography.subBodyR) .foregroundStyle(Alias.Color.Text.primary) Spacer() diff --git a/Projects/Shared/Utils/Sources/Extensions/String+.swift b/Projects/Shared/Utils/Sources/Extensions/String+.swift new file mode 100644 index 0000000..9bdffa2 --- /dev/null +++ b/Projects/Shared/Utils/Sources/Extensions/String+.swift @@ -0,0 +1,22 @@ +// +// String+.swift +// Utils +// +// Created by 김지현 on 8/16/24. +// Copyright © 2024 PomoNyang. All rights reserved. +// + +import Foundation + +extension String { + public func camelCased() -> String { + let components = self.lowercased().split(separator: "_") + let camelCased = components + .enumerated() + .map { index, component in + index == 0 ? String(component) : component.capitalized + } + .joined() + return camelCased + } +} From eb8a4e0bd3d3d7267be0b960d00e5f479fa4d17d Mon Sep 17 00:00:00 2001 From: Jihyun247 Date: Fri, 16 Aug 2024 20:28:16 +0900 Subject: [PATCH 12/14] feature: userNotification request --- .../OnboardingFeature/Sources/SelectCat/SelectCatCore.swift | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Projects/Feature/OnboardingFeature/Sources/SelectCat/SelectCatCore.swift b/Projects/Feature/OnboardingFeature/Sources/SelectCat/SelectCatCore.swift index 94b8fb1..5a69733 100644 --- a/Projects/Feature/OnboardingFeature/Sources/SelectCat/SelectCatCore.swift +++ b/Projects/Feature/OnboardingFeature/Sources/SelectCat/SelectCatCore.swift @@ -8,6 +8,7 @@ import APIClientInterface import UserServiceInterface +import UserNotificationClientInterface import Shared import ComposableArchitecture @@ -35,6 +36,7 @@ public struct SelectCatCore { @Dependency(APIClient.self) var apiClient @Dependency(UserService.self) var userService + @Dependency(UserNotificationClient.self) var userNotificationClient public var body: some ReducerOf { BindingReducer() @@ -75,6 +77,7 @@ public struct SelectCatCore { return .run { send in _ = try await userService.selectCat(no: selectedCat.no, apiClient: apiClient) // user notification 요청 + _ = try await userNotificationClient.requestAuthorization([.alert, .badge, .sound]) // go to naming cat } From dfb2a3dfa61de5c2c48a2ece33734ca8b37d3956 Mon Sep 17 00:00:00 2001 From: Jihyun247 Date: Fri, 16 Aug 2024 23:49:20 +0900 Subject: [PATCH 13/14] =?UTF-8?q?fix:=20=EC=BD=94=EB=93=9C=EB=A6=AC?= =?UTF-8?q?=EB=B7=B0=20=EB=B0=98=EC=98=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../APIClient/Sources/TokenInterceptor.swift | 2 +- .../Sources/API/AuthAPIClient.swift | 11 ++++++--- .../Sources/Onboarding/OnboardingItem.swift | 24 ------------------- .../Sources/SelectCat/CatFactory.swift | 10 -------- .../SelectCat/CatFactoryProtocol.swift | 19 +++++++++++++++ .../Sources/SelectCat/SelectCatCore.swift | 1 - .../Sources/SelectCat/SelectCatView.swift | 3 +-- .../{String+.swift => String+Extension.swift} | 0 8 files changed, 29 insertions(+), 41 deletions(-) create mode 100644 Projects/Feature/OnboardingFeature/Sources/SelectCat/CatFactoryProtocol.swift rename Projects/Shared/Utils/Sources/Extensions/{String+.swift => String+Extension.swift} (100%) diff --git a/Projects/Core/APIClient/Sources/TokenInterceptor.swift b/Projects/Core/APIClient/Sources/TokenInterceptor.swift index b45deae..dc46ca5 100644 --- a/Projects/Core/APIClient/Sources/TokenInterceptor.swift +++ b/Projects/Core/APIClient/Sources/TokenInterceptor.swift @@ -12,7 +12,7 @@ import APIClientInterface import Logger import Dependencies -public enum KeychainClientKeys: String { +enum KeychainClientKeys: String { case accessToken = "mohanyang_keychain_access_token" case refreshToken = "mohanyang_keychain_refresh_token" } diff --git a/Projects/Domain/AuthService/Sources/API/AuthAPIClient.swift b/Projects/Domain/AuthService/Sources/API/AuthAPIClient.swift index cead1cd..2620bec 100644 --- a/Projects/Domain/AuthService/Sources/API/AuthAPIClient.swift +++ b/Projects/Domain/AuthService/Sources/API/AuthAPIClient.swift @@ -12,6 +12,11 @@ import UserDefaultsClientInterface import AuthServiceInterface import Dependencies +enum KeychainClientKeys: String { + case accessToken = "mohanyang_keychain_access_token" + case refreshToken = "mohanyang_keychain_refresh_token" +} + extension AuthService: DependencyKey { public static let liveValue: AuthService = .live() private static func live() -> Self { @@ -26,15 +31,15 @@ extension AuthService: DependencyKey { isWithInterceptor: false ) - _ = keychainClient.create(key: "mohanyang_keychain_access_token", data: response.accessToken) - _ = keychainClient.create(key: "mohanyang_keychain_refresh_token", data: response.refreshToken) + _ = keychainClient.create(key: KeychainClientKeys.accessToken.rawValue, data: response.accessToken) + _ = keychainClient.create(key: KeychainClientKeys.refreshToken.rawValue, data: response.refreshToken) return } ) } private static func isTokenValid(_ keychainClient: KeychainClient) -> Bool { - let isTokenExist = keychainClient.read(key: "mohanyang_keychain_access_token") + let isTokenExist = keychainClient.read(key: KeychainClientKeys.accessToken.rawValue) return isTokenExist != nil } } diff --git a/Projects/Feature/OnboardingFeature/Sources/Onboarding/OnboardingItem.swift b/Projects/Feature/OnboardingFeature/Sources/Onboarding/OnboardingItem.swift index 173ae17..e38a710 100644 --- a/Projects/Feature/OnboardingFeature/Sources/Onboarding/OnboardingItem.swift +++ b/Projects/Feature/OnboardingFeature/Sources/Onboarding/OnboardingItem.swift @@ -32,27 +32,3 @@ let OnboardingItemsData: [OnboardingItem] = [ subTitle: "일정 시간 집중과 휴식을 반복해 번아웃을 방지하고\n짧은 시간의 몰입을 경험해보세요.") ] -public struct RandomAccessEquatableItems: RandomAccessCollection, Equatable where Element: Equatable { - var elements: [Element] - public init(elements: [Element]) { - self.elements = elements - } - - public var startIndex: Int { elements.startIndex } - public var endIndex: Int { elements.endIndex } - - public subscript(position: Int) -> Element { - return elements[position] - } - - public static func == (lhs: RandomAccessEquatableItems, rhs: RandomAccessEquatableItems) -> Bool { - guard lhs.count == rhs.count else { return false } - for (index, element) in lhs.enumerated() { - if element != rhs[index] { - return false - } - } - return true - } -} - diff --git a/Projects/Feature/OnboardingFeature/Sources/SelectCat/CatFactory.swift b/Projects/Feature/OnboardingFeature/Sources/SelectCat/CatFactory.swift index 5f42ca2..a2a4f33 100644 --- a/Projects/Feature/OnboardingFeature/Sources/SelectCat/CatFactory.swift +++ b/Projects/Feature/OnboardingFeature/Sources/SelectCat/CatFactory.swift @@ -13,16 +13,6 @@ public enum CatType: String, Equatable { case cheese, black, threeColor } -public protocol CatFactoryProtocol { - var id: String { get } // catType의 rawValue - var no: Int { get } // 서버에서 주는 no - var name: String { get } // 서버에서 주는 이름 - var keyword: String { get } - var keywordImage: Image { get } // 키워드에 따른 아이콘 이미지 - var catImage: Image { get } - var pushNotificationTitle: String { get } // 푸시알림 예시 글귀 -} - // MARK: ANY CAT public struct AnyCat: CatFactoryProtocol, Identifiable, Equatable { private let base: CatFactoryProtocol diff --git a/Projects/Feature/OnboardingFeature/Sources/SelectCat/CatFactoryProtocol.swift b/Projects/Feature/OnboardingFeature/Sources/SelectCat/CatFactoryProtocol.swift new file mode 100644 index 0000000..a63f89f --- /dev/null +++ b/Projects/Feature/OnboardingFeature/Sources/SelectCat/CatFactoryProtocol.swift @@ -0,0 +1,19 @@ +// +// CatFactoryProtocol.swift +// OnboardingFeature +// +// Created by 김지현 on 8/16/24. +// Copyright © 2024 PomoNyang. All rights reserved. +// + +import SwiftUI + +public protocol CatFactoryProtocol { + var id: String { get } // catType의 rawValue + var no: Int { get } // 서버에서 주는 no + var name: String { get } // 서버에서 주는 이름 + var keyword: String { get } + var keywordImage: Image { get } // 키워드에 따른 아이콘 이미지 + var catImage: Image { get } + var pushNotificationTitle: String { get } // 푸시알림 예시 글귀 +} diff --git a/Projects/Feature/OnboardingFeature/Sources/SelectCat/SelectCatCore.swift b/Projects/Feature/OnboardingFeature/Sources/SelectCat/SelectCatCore.swift index 5a69733..e165417 100644 --- a/Projects/Feature/OnboardingFeature/Sources/SelectCat/SelectCatCore.swift +++ b/Projects/Feature/OnboardingFeature/Sources/SelectCat/SelectCatCore.swift @@ -62,7 +62,6 @@ public struct SelectCatCore { } case ._fetchCatListResponse(let catList): - print(CatType.cheese.rawValue, CatType.black.rawValue, CatType.threeColor.rawValue) state.catList = catList.map { cat in CatFactory.makeCat( type: CatType(rawValue: cat.type.camelCased()) ?? .cheese, diff --git a/Projects/Feature/OnboardingFeature/Sources/SelectCat/SelectCatView.swift b/Projects/Feature/OnboardingFeature/Sources/SelectCat/SelectCatView.swift index 3b3b99e..5ce48e1 100644 --- a/Projects/Feature/OnboardingFeature/Sources/SelectCat/SelectCatView.swift +++ b/Projects/Feature/OnboardingFeature/Sources/SelectCat/SelectCatView.swift @@ -22,8 +22,7 @@ public struct SelectCatView: View { public var body: some View { NavigationContainer( title: Text("고양이 선택"), - style: .navigation, - navBackground: .clear + style: .navigation ) { VStack(spacing: 0) { HStack { diff --git a/Projects/Shared/Utils/Sources/Extensions/String+.swift b/Projects/Shared/Utils/Sources/Extensions/String+Extension.swift similarity index 100% rename from Projects/Shared/Utils/Sources/Extensions/String+.swift rename to Projects/Shared/Utils/Sources/Extensions/String+Extension.swift From 90bd8cd659af150aa550bc99ce0c550e44d45e1e Mon Sep 17 00:00:00 2001 From: Jihyun247 Date: Fri, 16 Aug 2024 23:59:30 +0900 Subject: [PATCH 14/14] =?UTF-8?q?fix:=20device=20ID=20=EC=83=81=EC=88=98?= =?UTF-8?q?=EB=A1=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Projects/Feature/SplashFeature/Sources/SplashCore.swift | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Projects/Feature/SplashFeature/Sources/SplashCore.swift b/Projects/Feature/SplashFeature/Sources/SplashCore.swift index 3811e5e..39b01e5 100644 --- a/Projects/Feature/SplashFeature/Sources/SplashCore.swift +++ b/Projects/Feature/SplashFeature/Sources/SplashCore.swift @@ -33,6 +33,8 @@ public struct SplashCore { public init() { } + let deviceIDKey = "mohanyang_keychain_device_id" + @Dependency(APIClient.self) var apiClient @Dependency(AuthService.self) var authService @Dependency(DatabaseClient.self) var databaseClient @@ -60,7 +62,7 @@ public struct SplashCore { extension SplashCore { private func checkDeviceIDExist() -> Effect { - let deviceID = keychainClient.read(key: "mohanyang_keychain_device_id") ?? getDeviceUUID() + let deviceID = keychainClient.read(key: deviceIDKey) ?? getDeviceUUID() return login(deviceID: deviceID) }