diff --git a/Plugins/DependencyPlugin/ProjectDescriptionHelpers/Modules.swift b/Plugins/DependencyPlugin/ProjectDescriptionHelpers/Modules.swift index 9f4730b8..254f6512 100644 --- a/Plugins/DependencyPlugin/ProjectDescriptionHelpers/Modules.swift +++ b/Plugins/DependencyPlugin/ProjectDescriptionHelpers/Modules.swift @@ -27,6 +27,7 @@ public extension ModulePath { // MARK: - FeatureModule public extension ModulePath { enum Feature: String, CaseIterable { + case Guide case TabBar case Report case BottleArrival diff --git a/Projects/App/Sources/AppDelegate.swift b/Projects/App/Sources/AppDelegate.swift index 1a599a09..1b193461 100644 --- a/Projects/App/Sources/AppDelegate.swift +++ b/Projects/App/Sources/AppDelegate.swift @@ -44,7 +44,7 @@ extension AppDelegate: UNUserNotificationCenterDelegate { Log.debug("fcm token: \(fcmToken ?? "NO TOKEN")") if let fcmToken { // TODO: user defaults 설정 방법 변경 - UserDefaults.standard.set(fcmToken, forKey: "fcmToken") + store.send(.appDelegate(.didReceivedFcmToken(fcmToken: fcmToken))) } } diff --git a/Projects/Domain/Auth/Sources/AuthClient.swift b/Projects/Domain/Auth/Sources/AuthClient.swift index 581a331e..d0c0b081 100644 --- a/Projects/Domain/Auth/Sources/AuthClient.swift +++ b/Projects/Domain/Auth/Sources/AuthClient.swift @@ -8,6 +8,8 @@ import Foundation import DomainAuthInterface +import DomainUser + import CoreNetwork import CoreLoggerInterface @@ -19,12 +21,13 @@ extension AuthClient: DependencyKey { private static func live() -> AuthClient { @Dependency(\.network) var networkManager @Dependency(\.loginManager) var loginManager - + @Dependency(\.userClient) var userClient return .init( signInWithKakao: { let signInResult = try await loginManager.signIn(loginType: .kakao) let accessToken = signInResult.accessToken - guard let fcmToken = UserDefaults.standard.string(forKey: "fcmToken") + let fcmToken = userClient.fetchFcmToken() + guard let fcmToken = fcmToken else { Log.fault("no fcm token") fatalError() @@ -40,8 +43,8 @@ extension AuthClient: DependencyKey { let signInResult = try await loginManager.signIn(loginType: .apple) let accessToken = signInResult.accessToken let userName = signInResult.userName - - guard let fcmToken = UserDefaults.standard.string(forKey: "fcmToken") + let fcmToken = userClient.fetchFcmToken() + guard let fcmToken = fcmToken else { Log.fault("no fcm token") fatalError() @@ -62,7 +65,8 @@ extension AuthClient: DependencyKey { try await networkManager.reqeust(api: .apiType(AuthAPI.withdraw)) }, logout: { - guard let fcmToken = UserDefaults.standard.string(forKey: "fcmToken") + let fcmToken = userClient.fetchFcmToken() + guard let fcmToken = fcmToken else { Log.fault("no fcm token") return diff --git a/Projects/Domain/User/Interface/Sources/UserClient.swift b/Projects/Domain/User/Interface/Sources/UserClient.swift index c140a255..dadc0abd 100644 --- a/Projects/Domain/User/Interface/Sources/UserClient.swift +++ b/Projects/Domain/User/Interface/Sources/UserClient.swift @@ -10,19 +10,25 @@ import Foundation public struct UserClient { private let _isLoggedIn: () -> Bool private let _isAppDeleted: () -> Bool + private let _fetchFcmToken: () -> String? private let updateLoginState: (Bool) -> Void private let updateDeleteState: (Bool) -> Void + private let updateFcmToken: (String) -> Void public init( isLoggedIn: @escaping () -> Bool, isAppDeleted: @escaping () -> Bool, + fetchFcmToken: @escaping () -> String?, updateLoginState: @escaping (Bool) -> Void, - updateDeleteState: @escaping (Bool) -> Void + updateDeleteState: @escaping (Bool) -> Void, + updateFcmToken: @escaping (String) -> Void ) { self._isLoggedIn = isLoggedIn self._isAppDeleted = isAppDeleted + self._fetchFcmToken = fetchFcmToken self.updateLoginState = updateLoginState self.updateDeleteState = updateDeleteState + self.updateFcmToken = updateFcmToken } public func isLoggedIn() -> Bool { @@ -33,6 +39,10 @@ public struct UserClient { _isAppDeleted() } + public func fetchFcmToken() -> String? { + _fetchFcmToken() + } + public func updateLoginState(isLoggedIn: Bool) { updateLoginState(isLoggedIn) } @@ -40,4 +50,8 @@ public struct UserClient { public func updateDeleteState(isDelete: Bool) { updateDeleteState(isDelete) } + + public func updateFcmToken(fcmToken: String) { + updateFcmToken(fcmToken) + } } diff --git a/Projects/Domain/User/Sources/UserClient.swift b/Projects/Domain/User/Sources/UserClient.swift index 96f0107f..6df5fad8 100644 --- a/Projects/Domain/User/Sources/UserClient.swift +++ b/Projects/Domain/User/Sources/UserClient.swift @@ -26,12 +26,20 @@ extension UserClient: DependencyKey { return !UserDefaults.standard.bool(forKey: "deleteState") }, + fetchFcmToken: { + return UserDefaults.standard.string(forKey: "fcmToken") + }, + updateLoginState: { isLoggedIn in UserDefaults.standard.set(isLoggedIn, forKey: "loginState") }, updateDeleteState: { isDelete in UserDefaults.standard.set(!isDelete, forKey: "deleteState") + }, + + updateFcmToken: { fcmToken in + UserDefaults.standard.set(fcmToken, forKey: "fcmToken") } ) } diff --git a/Projects/Feature/BottleStorage/Example/Sources/AppView.swift b/Projects/Feature/BottleStorage/Example/Sources/AppView.swift index ef784883..e05e0e8c 100644 --- a/Projects/Feature/BottleStorage/Example/Sources/AppView.swift +++ b/Projects/Feature/BottleStorage/Example/Sources/AppView.swift @@ -21,12 +21,6 @@ struct AppView: App { initialState: BottleStorageFeature.State(), reducer: { BottleStorageFeature() } )) - .onAppear { - AuthClient.liveValue.saveToken(token: .init( - accessToken: "eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxIiwiaWF0IjoxNzIzMTE3ODk1LCJleHAiOjE3MjMxNTM4OTV9.HjjnS1onaAUA6nJGOV-f6FE55eAihUGTFNYGmmyETQc", - refershToken: "eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxIiwiaWF0IjoxNzIzMTE3ODk1LCJleHAiOjE3Mzc2MzMwOTV9.Af-L2h_5pBQWrZCc1OQI3tm1DGwowqCAId-rK5vAPaQ" - )) - } } } } diff --git a/Projects/Feature/BottleStorage/Interface/Sources/PingPongDetail/View/PingPongDetail/PingPongDetailFeature.swift b/Projects/Feature/BottleStorage/Interface/Sources/PingPongDetail/View/PingPongDetail/PingPongDetailFeature.swift index cc01fbd4..d1ee40f0 100644 --- a/Projects/Feature/BottleStorage/Interface/Sources/PingPongDetail/View/PingPongDetail/PingPongDetailFeature.swift +++ b/Projects/Feature/BottleStorage/Interface/Sources/PingPongDetail/View/PingPongDetail/PingPongDetailFeature.swift @@ -54,6 +54,8 @@ extension PingPongDetailFeature { return fetchPingPong(state: &state) case .popToRootDidRequired: return .send(.delegate(.popToRootDidRequired)) + case .refreshPingPong: + return fetchPingPong(state: &state) } case let .matching(.delegate(delegate)): diff --git a/Projects/Feature/BottleStorage/Interface/Sources/PingPongDetail/View/SubViews/Matching/MatchingView.swift b/Projects/Feature/BottleStorage/Interface/Sources/PingPongDetail/View/SubViews/Matching/MatchingView.swift index 26f248b1..ad0d259a 100644 --- a/Projects/Feature/BottleStorage/Interface/Sources/PingPongDetail/View/SubViews/Matching/MatchingView.swift +++ b/Projects/Feature/BottleStorage/Interface/Sources/PingPongDetail/View/SubViews/Matching/MatchingView.swift @@ -13,6 +13,7 @@ import ComposableArchitecture public struct MatchingView: View { @Perception.Bindable private var store: StoreOf + @Environment(\.openURL) var openURL public init(store: StoreOf) { self.store = store @@ -175,7 +176,13 @@ private extension MatchingView { case .waiting: EmptyView() case .success: - EmptyView() + SolidButton( + title: "카카오톡 바로가기", + sizeType: .large, + buttonType: .throttle, + action: { openKakaoTalk() } + ) + case .failure: SolidButton( title: "다른 보틀 열어보기", @@ -242,3 +249,12 @@ private extension MatchingView { .padding(.bottom, 32.0) } } + +private extension MatchingView { + // TODO: 추후 구조 변경 필요 + func openKakaoTalk() { + let kakaoTalk = "kakaotalk://" + guard let kakaoTalkURL = NSURL(string: kakaoTalk) as? URL else { return } + openURL(kakaoTalkURL) + } +} diff --git a/Projects/Feature/BottleStorage/Interface/Sources/PingPongDetail/View/SubViews/QuestionAndAnswer/QuestionAndAnswerFeature.swift b/Projects/Feature/BottleStorage/Interface/Sources/PingPongDetail/View/SubViews/QuestionAndAnswer/QuestionAndAnswerFeature.swift index a2aa05db..5a4dc213 100644 --- a/Projects/Feature/BottleStorage/Interface/Sources/PingPongDetail/View/SubViews/QuestionAndAnswer/QuestionAndAnswerFeature.swift +++ b/Projects/Feature/BottleStorage/Interface/Sources/PingPongDetail/View/SubViews/QuestionAndAnswer/QuestionAndAnswerFeature.swift @@ -82,7 +82,7 @@ extension QuestionAndAnswerFeature { } } - case .stopTalkButtonTapped: + case .stopTalkButtonDidTapped: state.destination = .alert(.init( title: { TextState("중단하기") }, actions: { @@ -94,6 +94,9 @@ extension QuestionAndAnswerFeature { message: { TextState("중단 시 모든 핑퐁 내용이 사라져요. 정말 중단하시겠어요?") } )) return .none + + case .refreshDidPulled: + return .send(.delegate(.refreshPingPong)) case let .destination(.presented(.alert(alert))): switch alert { diff --git a/Projects/Feature/BottleStorage/Interface/Sources/PingPongDetail/View/SubViews/QuestionAndAnswer/QuestionAndAnswerFeatureInterface.swift b/Projects/Feature/BottleStorage/Interface/Sources/PingPongDetail/View/SubViews/QuestionAndAnswer/QuestionAndAnswerFeatureInterface.swift index 325aa352..4353b898 100644 --- a/Projects/Feature/BottleStorage/Interface/Sources/PingPongDetail/View/SubViews/QuestionAndAnswer/QuestionAndAnswerFeatureInterface.swift +++ b/Projects/Feature/BottleStorage/Interface/Sources/PingPongDetail/View/SubViews/QuestionAndAnswer/QuestionAndAnswerFeatureInterface.swift @@ -204,7 +204,8 @@ public struct QuestionAndAnswerFeature { case finalSelectButtonDidTapped(willMatch: Bool) case refreshPingPongDidRequired case configureShowLoadingIndicatorRequired(isShow: Bool) - case stopTalkButtonTapped + case stopTalkButtonDidTapped + case refreshDidPulled // ETC. case binding(BindingAction) @@ -220,6 +221,7 @@ public struct QuestionAndAnswerFeature { public enum Delegate { case reloadPingPongRequired case popToRootDidRequired + case refreshPingPong } } diff --git a/Projects/Feature/BottleStorage/Interface/Sources/PingPongDetail/View/SubViews/QuestionAndAnswer/QuestionAndAnswerView.swift b/Projects/Feature/BottleStorage/Interface/Sources/PingPongDetail/View/SubViews/QuestionAndAnswer/QuestionAndAnswerView.swift index 8123ba2f..b8208f68 100644 --- a/Projects/Feature/BottleStorage/Interface/Sources/PingPongDetail/View/SubViews/QuestionAndAnswer/QuestionAndAnswerView.swift +++ b/Projects/Feature/BottleStorage/Interface/Sources/PingPongDetail/View/SubViews/QuestionAndAnswer/QuestionAndAnswerView.swift @@ -103,7 +103,7 @@ public struct QuestionAndAnswerView: View { color: .enableSecondary ) .asThrottleButton { - store.send(.stopTalkButtonTapped) + store.send(.stopTalkButtonDidTapped) } .padding(.top, 12.0) .disabled(store.isStopped == true) @@ -122,6 +122,10 @@ public struct QuestionAndAnswerView: View { isTextFieldFocused = textFieldState == .active || textFieldState == .enabled ? false : true } } + .refreshable { + try? await Task.sleep(nanoseconds: 300_000_000) + await store.send(.refreshDidPulled).finish() + } .scrollIndicators(.hidden) .overlay { if store.isShowLoadingIndicator { diff --git a/Projects/Feature/Guide/Example/Sources/AppView.swift b/Projects/Feature/Guide/Example/Sources/AppView.swift new file mode 100644 index 00000000..22c8fed2 --- /dev/null +++ b/Projects/Feature/Guide/Example/Sources/AppView.swift @@ -0,0 +1,35 @@ +import SwiftUI + +import FeatureGuideInterface + +import ComposableArchitecture + +@main +struct AppView: App { + let mainGuideStore = Store( + initialState: MainGuideFeature.State(), + reducer: { MainGuideFeature() }) + + let pingpongGuideStore = Store( + initialState: PingPongGuideFeature.State(), + reducer: { PingPongGuideFeature() }) + + let photoShareGuideStore = Store( + initialState: PhotoShareGuideFeature.State(), + reducer: { PhotoShareGuideFeature() }) + + let startGuideStore = Store( + initialState: StartGuideFeature.State(), + reducer: { StartGuideFeature() }) + + var body: some Scene { + + WindowGroup { +// MainGuideView(store: mainGuideStore) +// PingPongGuideView(store: pingpongGuideStore) +// PhotoShareGuideView(store: photoShareGuideStore) + StartGuideView(store: startGuideStore) + } + } +} + diff --git a/Projects/Feature/Guide/Interface/Sources/MainGuide/MainGuideFeature.swift b/Projects/Feature/Guide/Interface/Sources/MainGuide/MainGuideFeature.swift new file mode 100644 index 00000000..f7d0b31e --- /dev/null +++ b/Projects/Feature/Guide/Interface/Sources/MainGuide/MainGuideFeature.swift @@ -0,0 +1,63 @@ +// +// MainGuideFeature.swift +// FeatureGuideInterface +// +// Created by 임현규 on 8/22/24. +// + +import Foundation + +import ComposableArchitecture + +@Reducer +public struct MainGuideFeature { + private let reducer: Reduce + + init(reducer: Reduce) { + self.reducer = reducer + } + + public struct State: Equatable { + public init() {} + } + + public enum Action { + + // User Action + case nextButtonDidTapped + case backButtonDidTapped + + // Delegate + case delegate(Delegate) + + public enum Delegate { + case nextButtonDidTapped + } + } + + public var body: some ReducerOf { + reducer + } +} + +extension MainGuideFeature { + public init() { + @Dependency(\.dismiss) var dismiss + let reducer = Reduce { state, action in + switch action { + case .nextButtonDidTapped: + return .send(.delegate(.nextButtonDidTapped)) + + case .backButtonDidTapped: + return .run { send in + await dismiss() + } + + default: + return .none + } + } + + self.init(reducer: reducer) + } +} diff --git a/Projects/Feature/Guide/Interface/Sources/MainGuide/MainGuideView.swift b/Projects/Feature/Guide/Interface/Sources/MainGuide/MainGuideView.swift new file mode 100644 index 00000000..83aeaf50 --- /dev/null +++ b/Projects/Feature/Guide/Interface/Sources/MainGuide/MainGuideView.swift @@ -0,0 +1,88 @@ +// +// MainGuideView.swift +// FeatureGuideInterface +// +// Created by 임현규 on 8/22/24. +// + +import SwiftUI + +import SharedDesignSystem + +import ComposableArchitecture + +public struct MainGuideView: View { + private let store: StoreOf + + public init(store: StoreOf) { + self.store = store + } + + public var body: some View { + + VStack(spacing: 0) { + title + ZStack(alignment: .bottom) { + GeometryReader { geometry in + let width = geometry.size.width + let horizontalPadding: CGFloat = 36 + let imageTopPadding: CGFloat = 48 + let bottleArrivalImageWidth = max(width - horizontalPadding * 2, 0) + HStack(spacing: 0) { + Spacer() + bottleArrivalImage + .aspectRatio(contentMode: .fill) + .frame(width: bottleArrivalImageWidth) + .clipped() + .offset(y: imageTopPadding) + Spacer() + } + } + nextButton + .padding(.bottom, .lg) + .background(to: ColorToken.container(.primary)) + } + } + .padding(.top, .xl) + .padding(.horizontal, .md) + .setNavigationBar { + makeNaivgationleftButton { + store.send(.backButtonDidTapped) + } + } + .toolbar(.visible, for: .navigationBar) + } +} + +private extension MainGuideView { + var title: some View { + TitleView( + pageInfo: PageInfo(nowPage: 1, totalCount: 4), + title: "천천히 서로를 알아가는 소개팅\n보틀에 오신 것을 환영해요!" + ) + } + + var bottleArrivalImage: some View { + BottleImageView(type: + .local(bottleImageSystem: .illustraition(.bottleArrivalPhone)) + ) + .clipShape(RoundedRectangle(cornerRadius: 30)) + .overlay( + RoundedRectangle(cornerRadius: 30) + .strokeBorder( + ColorToken.text(.primary).color, + lineWidth: 10 + ) + ) + } + + var nextButton: some View { + SolidButton( + title: "다음", + sizeType: .large, + buttonType: .throttle, + action: { store.send(.nextButtonDidTapped) } + ) + .shadow(color: .white, radius: 15, y: -30) + } +} diff --git a/Projects/Feature/Guide/Interface/Sources/PhotoShareGuide/PhotoShareGuideFeature.swift b/Projects/Feature/Guide/Interface/Sources/PhotoShareGuide/PhotoShareGuideFeature.swift new file mode 100644 index 00000000..ddd277fe --- /dev/null +++ b/Projects/Feature/Guide/Interface/Sources/PhotoShareGuide/PhotoShareGuideFeature.swift @@ -0,0 +1,63 @@ +// +// PhotoShareGuideFeature.swift +// FeatureGuideInterface +// +// Created by 임현규 on 8/23/24. +// + +import Foundation + +import ComposableArchitecture + +@Reducer +public struct PhotoShareGuideFeature { + private let reducer: Reduce + + init(reducer: Reduce) { + self.reducer = reducer + } + + public struct State: Equatable { + public init() {} + } + + public enum Action { + + // User Action + case nextButtonDidTapped + case backButtonDidTapped + + // Delegate + case delegate(Delegate) + + public enum Delegate { + case nextButtonDidTapped + } + } + + public var body: some ReducerOf { + reducer + } +} + +extension PhotoShareGuideFeature { + public init() { + @Dependency(\.dismiss) var dismiss + let reducer = Reduce { state, action in + switch action { + case .nextButtonDidTapped: + return .send(.delegate(.nextButtonDidTapped)) + + case .backButtonDidTapped: + return .run { send in + await dismiss() + } + + default: + return .none + } + } + + self.init(reducer: reducer) + } +} diff --git a/Projects/Feature/Guide/Interface/Sources/PhotoShareGuide/PhotoShareGuideView.swift b/Projects/Feature/Guide/Interface/Sources/PhotoShareGuide/PhotoShareGuideView.swift new file mode 100644 index 00000000..d88f54e8 --- /dev/null +++ b/Projects/Feature/Guide/Interface/Sources/PhotoShareGuide/PhotoShareGuideView.swift @@ -0,0 +1,106 @@ +// +// PhotoShareGuideView.swift +// FeatureGuideInterface +// +// Created by 임현규 on 8/23/24. +// + +import SwiftUI + +import SharedDesignSystem + +import ComposableArchitecture + +public struct PhotoShareGuideView: View { + private let store: StoreOf + + public init(store: StoreOf) { + self.store = store + } + + public var body: some View { + VStack(spacing: 0) { + title + .padding(.horizontal, .md) + ZStack(alignment: .bottom) { + GeometryReader { geometry in + let width = geometry.size.width + let horizontalPadding: CGFloat = 50 + let imageSetTopPadding: CGFloat = 48 + let photoShareTopPadding: CGFloat = -85 + let photoShareImageWidth = max(width - horizontalPadding * 2, 0) + + VStack(spacing: 0) { + HStack(spacing: 0) { + girlImage + .aspectRatio(contentMode: .fit) + .clipped() + Spacer() + .frame(width: 12) + + boyImage + .aspectRatio(contentMode: .fit) + .clipped() + } + + photoShareImage + .aspectRatio(contentMode: .fit) + .frame(width: photoShareImageWidth) + .clipped() + .offset(y: photoShareTopPadding) + } + .offset(y: imageSetTopPadding) + } + nextButton + .padding(.bottom, .lg) + .padding(.horizontal, .md) + .background(to: ColorToken.container(.primary)) + } + } + .padding(.top, .xl) + .setNavigationBar { + makeNaivgationleftButton { + store.send(.backButtonDidTapped) + } + } + } +} + +private extension PhotoShareGuideView { + var title: some View { + TitleView( + pageInfo: PageInfo(nowPage: 3, totalCount: 4), + title: "확신이 생겼다면\n사진과 연락처를 주고 받아 보세요" + ) + } + + var girlImage: some View { + BottleImageView(type: + .local(bottleImageSystem: .illustraition(.girl)) + ) + .cornerRadius(.md, corenrs: [.topRight]) + } + + var boyImage: some View { + BottleImageView(type: + .local(bottleImageSystem: .illustraition(.boy)) + ) + .cornerRadius(.md, corenrs: [.topLeft]) + } + + var photoShareImage: some View { + BottleImageView(type: + .local(bottleImageSystem: .illustraition(.photoShare)) + ) + } + + var nextButton: some View { + SolidButton( + title: "다음", + sizeType: .large, + buttonType: .throttle, + action: { store.send(.nextButtonDidTapped) } + ) + .shadow(color: .white, radius: 15, y: -30) + } +} diff --git a/Projects/Feature/Guide/Interface/Sources/PingPongGuide/PingPongGuideFeature.swift b/Projects/Feature/Guide/Interface/Sources/PingPongGuide/PingPongGuideFeature.swift new file mode 100644 index 00000000..a790018e --- /dev/null +++ b/Projects/Feature/Guide/Interface/Sources/PingPongGuide/PingPongGuideFeature.swift @@ -0,0 +1,63 @@ +// +// PingPongGuideFeature.swift +// FeatureGuideInterface +// +// Created by 임현규 on 8/22/24. +// + +import Foundation + +import ComposableArchitecture + +@Reducer +public struct PingPongGuideFeature { + private let reducer: Reduce + + init(reducer: Reduce) { + self.reducer = reducer + } + + public struct State: Equatable { + public init() {} + } + + public enum Action { + + // User Action + case nextButtonDidTapped + case backButtonDidTapped + + // Delegate + case delegate(Delegate) + + public enum Delegate { + case nextButtonDidTapped + } + } + + public var body: some ReducerOf { + reducer + } +} + +extension PingPongGuideFeature { + public init() { + @Dependency(\.dismiss) var dismiss + let reducer = Reduce { state, action in + switch action { + case .nextButtonDidTapped: + return .send(.delegate(.nextButtonDidTapped)) + + case .backButtonDidTapped: + return .run { send in + await dismiss() + } + + default: + return .none + } + } + + self.init(reducer: reducer) + } +} diff --git a/Projects/Feature/Guide/Interface/Sources/PingPongGuide/PingPongGuideView.swift b/Projects/Feature/Guide/Interface/Sources/PingPongGuide/PingPongGuideView.swift new file mode 100644 index 00000000..2a7ff7bd --- /dev/null +++ b/Projects/Feature/Guide/Interface/Sources/PingPongGuide/PingPongGuideView.swift @@ -0,0 +1,95 @@ +// +// PingPongGuideView.swift +// FeatureGuideInterface +// +// Created by 임현규 on 8/22/24. +// + +import SwiftUI + +import SharedDesignSystem + +import ComposableArchitecture + +public struct PingPongGuideView: View { + private let store: StoreOf + + public init(store: StoreOf) { + self.store = store + UINavigationBar.setAnimationsEnabled(false) + } + + public var body: some View { + VStack(spacing: 0) { + title + ZStack(alignment: .bottom) { + GeometryReader { geometry in + let width = geometry.size.width + let firstHorizontalPadding: CGFloat = 35 + let firstPingPongWidth = max(width - firstHorizontalPadding * 2, 0) + let secondHorizontalPadding: CGFloat = 27.5 + let secondPingPongWidth = max(width - secondHorizontalPadding * 2, 0) + let imageSetTopPadding: CGFloat = 48 + + HStack(spacing: 0) { + Spacer() + VStack(spacing: 10) { + firstPingPongImage + .aspectRatio(contentMode: .fit) + .frame(width: firstPingPongWidth) + .clipped() + secondPingPongImage + .aspectRatio(contentMode: .fit) + .frame(width: secondPingPongWidth) + .clipped() + } + Spacer() + } + .offset(y: imageSetTopPadding) + } + nextButton + .padding(.bottom, .lg) + .background(to: ColorToken.container(.primary)) + } + } + .padding(.top, .xl) + .padding(.horizontal, .md) + .setNavigationBar { + makeNaivgationleftButton { + store.send(.backButtonDidTapped) + } + } + } +} + +private extension PingPongGuideView { + var title: some View { + TitleView( + pageInfo: PageInfo(nowPage: 2, totalCount: 4), + title: "보틀만의 가지관 문답을 통해\n진솔한 모습을 확인할 수 있어요" + ) + } + + var firstPingPongImage: some View { + BottleImageView(type: + .local(bottleImageSystem: .illustraition(.firstPingPong)) + ) + } + + var secondPingPongImage: some View { + BottleImageView(type: + .local(bottleImageSystem: .illustraition(.secondPingPong)) + ) + } + + var nextButton: some View { + SolidButton( + title: "다음", + sizeType: .large, + buttonType: .throttle, + action: { store.send(.nextButtonDidTapped) } + ) + .shadow(color: .white, radius: 15, y: -30) + + } +} diff --git a/Projects/Feature/Guide/Interface/Sources/StartGuide/StartGuideFeature.swift b/Projects/Feature/Guide/Interface/Sources/StartGuide/StartGuideFeature.swift new file mode 100644 index 00000000..afa30e20 --- /dev/null +++ b/Projects/Feature/Guide/Interface/Sources/StartGuide/StartGuideFeature.swift @@ -0,0 +1,63 @@ +// +// StartGuideFeature.swift +// FeatureGuideInterface +// +// Created by 임현규 on 8/23/24. +// + +import Foundation + +import ComposableArchitecture + +@Reducer +public struct StartGuideFeature { + private let reducer: Reduce + + init(reducer: Reduce) { + self.reducer = reducer + } + + public struct State: Equatable { + public init() {} + } + + public enum Action { + + // User Action + case doneButtonDidTapped + case backButtonDidTapped + + // Delegate + case delegate(Delegate) + + public enum Delegate { + case doneButtonDidTapped + } + } + + public var body: some ReducerOf { + reducer + } +} + +extension StartGuideFeature { + public init() { + @Dependency(\.dismiss) var dismiss + let reducer = Reduce { state, action in + switch action { + case .doneButtonDidTapped: + return .send(.delegate(.doneButtonDidTapped)) + + case .backButtonDidTapped: + return .run { send in + await dismiss() + } + + default: + return .none + } + } + + self.init(reducer: reducer) + } +} diff --git a/Projects/Feature/Guide/Interface/Sources/StartGuide/StartGuideView.swift b/Projects/Feature/Guide/Interface/Sources/StartGuide/StartGuideView.swift new file mode 100644 index 00000000..5204bc1a --- /dev/null +++ b/Projects/Feature/Guide/Interface/Sources/StartGuide/StartGuideView.swift @@ -0,0 +1,75 @@ +// +// StartGuideView.swift +// FeatureGuideInterface +// +// Created by 임현규 on 8/23/24. +// + +import SwiftUI + +import SharedDesignSystem + +import ComposableArchitecture + +public struct StartGuideView: View { + private let store: StoreOf + + public init(store: StoreOf) { + self.store = store + UINavigationBar.setAnimationsEnabled(true) + } + + public var body: some View { + VStack(spacing: 0) { + title + ZStack(alignment: .bottom) { + GeometryReader { geometry in + let bottleImageTopPadding: CGFloat = 48 + HStack(spacing: 0) { + Spacer() + bottleImage + .scaledToFit() + Spacer() + } + .offset(y: bottleImageTopPadding) + } + doneButton + .padding(.bottom, .lg) + .background(to: ColorToken.container(.primary)) + } + } + .padding(.top, .xl) + .padding(.horizontal, .md) + .setNavigationBar { + makeNaivgationleftButton { + store.send(.backButtonDidTapped) + } + } + } +} + +private extension StartGuideView { + var title: some View { + TitleView( + pageInfo: PageInfo(nowPage: 4, totalCount: 4), + title: "설레는 여정\n보틀과 함께 시작해 볼까요?" + ) + } + + var bottleImage: some View { + BottleImageView(type: + .local(bottleImageSystem: .illustraition(.bottle2)) + ) + } + + var doneButton: some View { + SolidButton( + title: "완료", + sizeType: .large, + buttonType: .throttle, + action: { store.send(.doneButtonDidTapped) } + ) + .shadow(color: .white, radius: 15, y: -30) + + } +} diff --git a/Projects/Feature/Guide/Project.swift b/Projects/Feature/Guide/Project.swift new file mode 100644 index 00000000..97997de9 --- /dev/null +++ b/Projects/Feature/Guide/Project.swift @@ -0,0 +1,52 @@ +import ProjectDescription +import ProjectDescriptionHelpers +import DependencyPlugin + +let project = Project.makeModule( + name: ModulePath.Feature.name+ModulePath.Feature.Guide.rawValue, + targets: [ + .feature( + interface: .Guide, + factory: .init(dependencies: [ + .core + ]) + ), + .feature( + implements: .Guide, + factory: .init( + dependencies: [ + .feature(interface: .Guide) + ] + ) + ), + + .feature( + testing: .Guide, + factory: .init( + dependencies: [ + .feature(interface: .Guide) + ] + ) + ), + .feature( + tests: .Guide, + factory: .init( + dependencies: [ + .feature(testing: .Guide), + .feature(implements: .Guide) + ] + ) + ), + + .feature( + example: .Guide, + factory: .init( + dependencies: [ + .feature(testing: .Guide), + .feature(implements: .Guide) + ] + ) + ) + + ] +) diff --git a/Projects/Feature/Guide/Sources/Sources.swift b/Projects/Feature/Guide/Sources/Sources.swift new file mode 100644 index 00000000..89c07008 --- /dev/null +++ b/Projects/Feature/Guide/Sources/Sources.swift @@ -0,0 +1,8 @@ +// +// Sources.swift +// FeatureGuide +// +// Created by 임현규 on 8/22/24. +// + +import Foundation diff --git a/Projects/Feature/Guide/Testing/Sources/GuideTesting.swift b/Projects/Feature/Guide/Testing/Sources/GuideTesting.swift new file mode 100644 index 00000000..b1853ce6 --- /dev/null +++ b/Projects/Feature/Guide/Testing/Sources/GuideTesting.swift @@ -0,0 +1 @@ +// This is for Tuist diff --git a/Projects/Feature/Guide/Tests/Sources/GuideTest.swift b/Projects/Feature/Guide/Tests/Sources/GuideTest.swift new file mode 100644 index 00000000..b3cdf63c --- /dev/null +++ b/Projects/Feature/Guide/Tests/Sources/GuideTest.swift @@ -0,0 +1,11 @@ +import XCTest + +final class GuideTests: XCTestCase { + override func setUpWithError() throws {} + + override func tearDownWithError() throws {} + + func testExample() { + XCTAssertEqual(1, 1) + } +} diff --git a/Projects/Feature/Login/Interface/Sources/Login/LoginFeature.swift b/Projects/Feature/Login/Interface/Sources/Login/LoginFeature.swift index 96e9b61e..2bd4b617 100644 --- a/Projects/Feature/Login/Interface/Sources/Login/LoginFeature.swift +++ b/Projects/Feature/Login/Interface/Sources/Login/LoginFeature.swift @@ -9,6 +9,7 @@ import Foundation import FeatureOnboardingInterface import FeatureGeneralSignUpInterface +import FeatureGuideInterface import DomainAuth import DomainAuthInterface @@ -47,7 +48,7 @@ extension LoginFeature { let userInfo = try await authClient.signInWithApple() await send(.socialLoginDidSuccess(userInfo)) // clientSceret 받아오기 - + let clientSceret = try await authClient.fetchAppleClientSecret() KeyChainTokenStore.shared.save(property: .AppleClientSecret, value: clientSceret) @@ -86,7 +87,7 @@ extension LoginFeature { } case .userProfileFetchDiduccess: - return goToOboarding(state: &state) + return .send(.signUpCheckCompleted(isSignUp: false)) case .goToGeneralLogin: // TODO: - 일반 로그인 화면으로 이동. @@ -96,14 +97,18 @@ extension LoginFeature { if isSignUp { return .send(.goToMainTab) } else { - return goToOboarding(state: &state) + return goToGuide(state: &state) } + case .guideDidCompleted: + return goToOboarding(state: &state) + case .snsLoginButtonDidTapped: state.path.append(.appleLogin(AppleLoginFeature.State())) return .none case .path(.element(id: _, action: .onBoarding(.delegate(.createOnboardingProfileDidCompleted)))): + state.path.removeAll() return .send(.delegate(.createOnboardingProfileDidCompleted)) case let .path(.element(id: _, action: .generalLogin(.delegate(delegate)))): @@ -118,6 +123,35 @@ extension LoginFeature { return handleLoginSuccessUserInfo(state: &state, userInfo: userInfo) } + // MainGuide Delegate + case let .path(.element(id: _, action: .mainGuide(.delegate(delegate)))): + switch delegate { + case .nextButtonDidTapped: + state.path.append(.pingPongGuide(.init())) + return .none + } + + // PingPongGuide Delegate + case let .path(.element(id: _, action: .pingPongGuide(.delegate(delegate)))): + switch delegate { + case .nextButtonDidTapped: + state.path.append(.photoShareGuide(.init())) + return .none + } + // PhotoShareGuide Delegate + + case let .path(.element(id: _, action: .photoShareGuide(.delegate(delegate)))): + switch delegate { + case .nextButtonDidTapped: + state.path.append(.startGuide(.init())) + return .none + } + // StartGuide Delegate + case let .path(.element(id: _, action: .startGuide(.delegate(delegate)))): + switch delegate { + case .doneButtonDidTapped: + return goToOboarding(state: &state) + } // appleLogin Delegate case let .path(.element(id: _, action: .appleLogin(.delegate(delegate)))): @@ -130,6 +164,10 @@ extension LoginFeature { } // MARK: - Inner Methods + func goToGuide(state: inout LoginFeature.State) -> Effect { + state.path.append(.mainGuide(.init())) + return .none + } func goToOboarding(state: inout LoginFeature.State) -> Effect { state.path.append(.onBoarding(.init())) @@ -167,6 +205,10 @@ extension LoginFeature { case generalLogin(GeneralLogInFeature) case generalSignUp(GeneralSignUpFeature) case onBoarding(OnboardingFeature) + case mainGuide(MainGuideFeature) + case pingPongGuide(PingPongGuideFeature) + case photoShareGuide(PhotoShareGuideFeature) + case startGuide(StartGuideFeature) case appleLogin(AppleLoginFeature) } diff --git a/Projects/Feature/Login/Interface/Sources/Login/LoginFeatureInterface.swift b/Projects/Feature/Login/Interface/Sources/Login/LoginFeatureInterface.swift index 0b70a476..cc1b69a5 100644 --- a/Projects/Feature/Login/Interface/Sources/Login/LoginFeatureInterface.swift +++ b/Projects/Feature/Login/Interface/Sources/Login/LoginFeatureInterface.swift @@ -49,6 +49,7 @@ public struct LoginFeature { case personalInformationTermButtonDidTapped case utilizationTermButtonDidTapped + case guideDidCompleted case indicatorStateChanged(isLoading: Bool) case socialLoginDidSuccess(UserInfo) case signUpCheckCompleted(isSignUp: Bool) diff --git a/Projects/Feature/Login/Interface/Sources/Login/LoginView.swift b/Projects/Feature/Login/Interface/Sources/Login/LoginView.swift index cc0100ec..3b585f70 100644 --- a/Projects/Feature/Login/Interface/Sources/Login/LoginView.swift +++ b/Projects/Feature/Login/Interface/Sources/Login/LoginView.swift @@ -8,11 +8,14 @@ import SwiftUI import AuthenticationServices -import SharedDesignSystem import FeatureOnboardingInterface import FeatureGeneralSignUpInterface +import FeatureGuideInterface + import CoreLoggerInterface +import SharedDesignSystem + import ComposableArchitecture public struct LoginView: View { @@ -25,41 +28,40 @@ public struct LoginView: View { public var body: some View { WithPerceptionTracking { NavigationStack(path: $store.scope(state: \.path, action: \.path)) { - VStack(spacing: 0) { - Spacer() - .frame(height: 52) - whiteLogo - .padding(.top, 52) - .padding(.bottom, .xl) - - mainText - - Spacer() - - VStack(spacing: 30.0) { - signInWithKakaoButton - snsLoginButton - } - .padding(.bottom, 30.0) + VStack(spacing: 0) { + Spacer() + .frame(height: 52) + whiteLogo + .padding(.top, 52) + .padding(.bottom, .xl) + + mainText + + Spacer() + + VStack(spacing: 30.0) { + signInWithKakaoButton + snsLoginButton } - .background { - BottleImageView( - type: .local(bottleImageSystem: .illustraition(.loginBackground)) - ) - } - .edgesIgnoringSafeArea([.top, .bottom]) - .sheet( - isPresented: $store.isPresentTermView, - content: { - TermsWebView(url: store.termURL) - } + .padding(.bottom, 30.0) + } + .background { + BottleImageView( + type: .local(bottleImageSystem: .illustraition(.loginBackground)) ) - .overlay { - if store.isLoading { - LoadingIndicator() - } + } + .edgesIgnoringSafeArea([.top, .bottom]) + .sheet( + isPresented: $store.isPresentTermView, + content: { + TermsWebView(url: store.termURL) } - + ) + .overlay { + if store.isLoading { + LoadingIndicator() + } + } } destination: { store in WithPerceptionTracking { switch store.state { @@ -67,15 +69,37 @@ public struct LoginView: View { if let store = store.scope(state: \.generalLogin, action: \.generalLogin) { GeneralLogInView(store: store) } + case .onBoarding: if let store = store.scope(state: \.onBoarding, action: \.onBoarding) { OnboardingView(store: store) } + case .generalSignUp: if let store = store.scope(state: \.generalSignUp, action: \.generalSignUp) { GeneralSignUpView(store: store) } + case .mainGuide: + if let store = store.scope(state: \.mainGuide, action: \.mainGuide) { + MainGuideView(store: store) + } + + case .pingPongGuide: + if let store = store.scope(state: \.pingPongGuide, action: \.pingPongGuide) { + PingPongGuideView(store: store) + } + + case .photoShareGuide: + if let store = store.scope(state: \.photoShareGuide, action: \.photoShareGuide) { + PhotoShareGuideView(store: store) + } + + case .startGuide: + if let store = store.scope(state: \.startGuide, action: \.startGuide) { + StartGuideView(store: store) + } + case .appleLogin: if let store = store.scope(state: \.appleLogin, action: \.appleLogin) { AppleLoginView(store: store) diff --git a/Projects/Feature/Login/Project.swift b/Projects/Feature/Login/Project.swift index 5f7d0259..8b4eae87 100644 --- a/Projects/Feature/Login/Project.swift +++ b/Projects/Feature/Login/Project.swift @@ -12,7 +12,8 @@ let project = Project.makeModule( .domain, .feature(interface: .BaseWebView), .feature(interface: .Onboarding), - .feature(interface: .GeneralSignUp) + .feature(interface: .GeneralSignUp), + .feature(interface: .Guide) ] ) ), diff --git a/Projects/Feature/Sources/App/AppDelegateFeature.swift b/Projects/Feature/Sources/App/AppDelegateFeature.swift index d2ec159c..4233bede 100644 --- a/Projects/Feature/Sources/App/AppDelegateFeature.swift +++ b/Projects/Feature/Sources/App/AppDelegateFeature.swift @@ -19,6 +19,14 @@ public struct AppDelegateFeature { public enum Action { case didFinishLunching + case didReceivedFcmToken(fcmToken: String) + + // Delegate + case delegate(Delegate) + + public enum Delegate { + case fcmTokenDidRecevied(fcmToken: String) + } } public var body: some ReducerOf { @@ -37,6 +45,14 @@ public struct AppDelegateFeature { KakaoSDK.initSDK(appKey: kakaoAppKey) return .none + + case let .didReceivedFcmToken(fcmToken): + return .run { send in + await send(.delegate(.fcmTokenDidRecevied(fcmToken: fcmToken))) + } + + default: + return .none } } } diff --git a/Projects/Feature/Sources/App/AppFeature.swift b/Projects/Feature/Sources/App/AppFeature.swift index a20dcc19..a5454124 100644 --- a/Projects/Feature/Sources/App/AppFeature.swift +++ b/Projects/Feature/Sources/App/AppFeature.swift @@ -52,6 +52,7 @@ public struct AppFeature { case login(LoginFeature.Action) case onboarding(OnboardingFeature.Action) + case checkUserLoginState case sceneDidActive case appleUserIdDidRevoked case loginCheckCompleted(isLoggedIn: Bool) @@ -85,7 +86,7 @@ public struct AppFeature { action: Action ) -> EffectOf { switch action { - case .onAppear: + case .checkUserLoginState: let isAppDeleted = userClient.isAppDeleted() let isLoggedIn = authClient.checkTokenIsExist() @@ -120,6 +121,14 @@ public struct AppFeature { return changeRoot(.Onboarding, state: &state) } + // AppDelegate Delegate + case let .appDelegate(.delegate(delegate)): + switch delegate { + case let .fcmTokenDidRecevied(fcmToken): + userClient.updateFcmToken(fcmToken: fcmToken) + return .send(.checkUserLoginState) + } + // Login Delegate case let .login(.delegate(delegate)): switch delegate { diff --git a/Projects/Shared/DesignSystem/Resources/Images.xcassets/illustration/illustraition_bottleArrivalPhone.imageset/Contents.json b/Projects/Shared/DesignSystem/Resources/Images.xcassets/illustration/illustraition_bottleArrivalPhone.imageset/Contents.json new file mode 100644 index 00000000..8e437b1a --- /dev/null +++ b/Projects/Shared/DesignSystem/Resources/Images.xcassets/illustration/illustraition_bottleArrivalPhone.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "illustraition_bottleArrivalPhone.svg", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Projects/Shared/DesignSystem/Resources/Images.xcassets/illustration/illustraition_bottleArrivalPhone.imageset/illustraition_bottleArrivalPhone.svg b/Projects/Shared/DesignSystem/Resources/Images.xcassets/illustration/illustraition_bottleArrivalPhone.imageset/illustraition_bottleArrivalPhone.svg new file mode 100644 index 00000000..9a50b810 --- /dev/null +++ b/Projects/Shared/DesignSystem/Resources/Images.xcassets/illustration/illustraition_bottleArrivalPhone.imageset/illustraition_bottleArrivalPhone.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/Projects/Shared/DesignSystem/Resources/Images.xcassets/illustration/illustraition_firstPingPong.imageset/Contents.json b/Projects/Shared/DesignSystem/Resources/Images.xcassets/illustration/illustraition_firstPingPong.imageset/Contents.json new file mode 100644 index 00000000..1b821574 --- /dev/null +++ b/Projects/Shared/DesignSystem/Resources/Images.xcassets/illustration/illustraition_firstPingPong.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "illustraition_firstPingPong.svg", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Projects/Shared/DesignSystem/Resources/Images.xcassets/illustration/illustraition_firstPingPong.imageset/illustraition_firstPingPong.svg b/Projects/Shared/DesignSystem/Resources/Images.xcassets/illustration/illustraition_firstPingPong.imageset/illustraition_firstPingPong.svg new file mode 100644 index 00000000..5b812454 --- /dev/null +++ b/Projects/Shared/DesignSystem/Resources/Images.xcassets/illustration/illustraition_firstPingPong.imageset/illustraition_firstPingPong.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/Projects/Shared/DesignSystem/Resources/Images.xcassets/illustration/illustraition_photoShare.imageset/Contents.json b/Projects/Shared/DesignSystem/Resources/Images.xcassets/illustration/illustraition_photoShare.imageset/Contents.json new file mode 100644 index 00000000..9eb2c884 --- /dev/null +++ b/Projects/Shared/DesignSystem/Resources/Images.xcassets/illustration/illustraition_photoShare.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "illustraition_photoShare.svg", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Projects/Shared/DesignSystem/Resources/Images.xcassets/illustration/illustraition_photoShare.imageset/illustraition_photoShare.svg b/Projects/Shared/DesignSystem/Resources/Images.xcassets/illustration/illustraition_photoShare.imageset/illustraition_photoShare.svg new file mode 100644 index 00000000..326442fc --- /dev/null +++ b/Projects/Shared/DesignSystem/Resources/Images.xcassets/illustration/illustraition_photoShare.imageset/illustraition_photoShare.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/Projects/Shared/DesignSystem/Resources/Images.xcassets/illustration/illustraition_secondPingPong.imageset/Contents.json b/Projects/Shared/DesignSystem/Resources/Images.xcassets/illustration/illustraition_secondPingPong.imageset/Contents.json new file mode 100644 index 00000000..ab1fd8fa --- /dev/null +++ b/Projects/Shared/DesignSystem/Resources/Images.xcassets/illustration/illustraition_secondPingPong.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "illustraition_secondPingPong.svg", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Projects/Shared/DesignSystem/Resources/Images.xcassets/illustration/illustraition_secondPingPong.imageset/illustraition_secondPingPong.svg b/Projects/Shared/DesignSystem/Resources/Images.xcassets/illustration/illustraition_secondPingPong.imageset/illustraition_secondPingPong.svg new file mode 100644 index 00000000..4d14d9a4 --- /dev/null +++ b/Projects/Shared/DesignSystem/Resources/Images.xcassets/illustration/illustraition_secondPingPong.imageset/illustraition_secondPingPong.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/Projects/Shared/DesignSystem/Sources/Image/BottleImageSystem+Illustraition.swift b/Projects/Shared/DesignSystem/Sources/Image/BottleImageSystem+Illustraition.swift index 2ec540a4..b7bad9ea 100644 --- a/Projects/Shared/DesignSystem/Sources/Image/BottleImageSystem+Illustraition.swift +++ b/Projects/Shared/DesignSystem/Sources/Image/BottleImageSystem+Illustraition.swift @@ -26,6 +26,10 @@ public extension Image.BottleImageSystem { case phone case basket case splash + case firstPingPong + case secondPingPong + case photoShare + case bottleArrivalPhone } } @@ -66,6 +70,14 @@ public extension Image.BottleImageSystem.Illustraition { return SharedDesignSystemAsset.Images.illustraitionBasket.swiftUIImage case .splash: return SharedDesignSystemAsset.Images.imageSplash.swiftUIImage + case .firstPingPong: + return SharedDesignSystemAsset.Images.illustraitionFirstPingPong.swiftUIImage + case .secondPingPong: + return SharedDesignSystemAsset.Images.illustraitionSecondPingPong.swiftUIImage + case .photoShare: + return SharedDesignSystemAsset.Images.illustraitionPhotoShare.swiftUIImage + case .bottleArrivalPhone: + return SharedDesignSystemAsset.Images.illustraitionBottleArrivalPhone.swiftUIImage } } } diff --git a/Tuist/ProjectDescriptionHelpers/InfoPlist+Templates.swift b/Tuist/ProjectDescriptionHelpers/InfoPlist+Templates.swift index 4f4a5380..91930e50 100644 --- a/Tuist/ProjectDescriptionHelpers/InfoPlist+Templates.swift +++ b/Tuist/ProjectDescriptionHelpers/InfoPlist+Templates.swift @@ -10,8 +10,8 @@ import ProjectDescription public extension InfoPlist { static var app: InfoPlist { return .extendingDefault(with: [ - "CFBundleShortVersionString": "1.0.2", - "CFBundleVersion": "21", + "CFBundleShortVersionString": "1.0.3", + "CFBundleVersion": "23", "UIUserInterfaceStyle": "Light", "CFBundleName": "보틀", "UILaunchScreen": [ @@ -25,7 +25,7 @@ public extension InfoPlist { "BASE_URL": "$(BASE_URL)", "WEB_VIEW_BASE_URL": "$(WEB_VIEW_BASE_URL)", "WEB_VIEW_MESSAGE_HANDLER_DEFAULT_NAME": "$(WEB_VIEW_MESSAGE_HANDLER_DEFAULT_NAME)", - "LSApplicationQueriesSchemes": ["kakaokompassauth"], + "LSApplicationQueriesSchemes": ["kakaokompassauth", "kakaotalk"], "CFBundleURLTypes": [ [ "CFBundleTypeRole": "Editor", @@ -39,8 +39,8 @@ public extension InfoPlist { static var example: InfoPlist { return .extendingDefault(with: [ - "CFBundleShortVersionString": "1.0.2", - "CFBundleVersion": "21", + "CFBundleShortVersionString": "1.0.3", + "CFBundleVersion": "23", "UIUserInterfaceStyle": "Light", "UILaunchScreen": [:], "UISupportedInterfaceOrientations": [ @@ -49,7 +49,7 @@ public extension InfoPlist { "BASE_URL": "$(BASE_URL)", "WEB_VIEW_BASE_URL": "$(WEB_VIEW_BASE_URL)", "WEB_VIEW_MESSAGE_HANDLER_DEFAULT_NAME": "$(WEB_VIEW_MESSAGE_HANDLER_DEFAULT_NAME)", - "LSApplicationQueriesSchemes": ["kakaokompassauth"], + "LSApplicationQueriesSchemes": ["kakaokompassauth", "kakaotalk"], "CFBundleURLTypes": [ [ "CFBundleTypeRole": "Editor",