diff --git a/DemoApps/ChatDemoApp/ChatDemoApp.xcodeproj/project.pbxproj b/DemoApps/ChatDemoApp/ChatDemoApp.xcodeproj/project.pbxproj index c613553..82e6a85 100644 --- a/DemoApps/ChatDemoApp/ChatDemoApp.xcodeproj/project.pbxproj +++ b/DemoApps/ChatDemoApp/ChatDemoApp.xcodeproj/project.pbxproj @@ -278,6 +278,7 @@ ENABLE_PREVIEWS = YES; HEADER_SEARCH_PATHS = ( "$(inherited)", + "$(SRCROOT)/../../Tuist/.build/checkouts/FSCalendar/FSCalendar/include", "$(SRCROOT)/../../Tuist/.build/checkouts/RxSwift/Sources/RxCocoaRuntime/include", ); INFOPLIST_FILE = "Derived/InfoPlists/ChatDemoApp-Info.plist"; @@ -287,11 +288,14 @@ ); OTHER_CFLAGS = ( "$(inherited)", + "-fmodule-map-file=$(SRCROOT)/../../Tuist/.build/checkouts/FSCalendar/FSCalendar/include/module.modulemap", "-fmodule-map-file=$(SRCROOT)/../../Tuist/.build/tuist-derived/RxCocoaRuntime/RxCocoaRuntime.modulemap", ); OTHER_SWIFT_FLAGS = ( "$(inherited)", "-Xcc", + "-fmodule-map-file=$(SRCROOT)/../../Tuist/.build/checkouts/FSCalendar/FSCalendar/include/module.modulemap", + "-Xcc", "-fmodule-map-file=$(SRCROOT)/../../Tuist/.build/tuist-derived/RxCocoaRuntime/RxCocoaRuntime.modulemap", ); PRODUCT_BUNDLE_IDENTIFIER = io.tuist.ChatDemoApp; @@ -377,6 +381,7 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; HEADER_SEARCH_PATHS = ( "$(inherited)", + "$(SRCROOT)/../../Tuist/.build/checkouts/FSCalendar/FSCalendar/include", "$(SRCROOT)/../../Tuist/.build/checkouts/RxSwift/Sources/RxCocoaRuntime/include", ); INFOPLIST_FILE = "Derived/InfoPlists/ChatDemoAppTests-Info.plist"; @@ -387,11 +392,14 @@ ); OTHER_CFLAGS = ( "$(inherited)", + "-fmodule-map-file=$(SRCROOT)/../../Tuist/.build/checkouts/FSCalendar/FSCalendar/include/module.modulemap", "-fmodule-map-file=$(SRCROOT)/../../Tuist/.build/tuist-derived/RxCocoaRuntime/RxCocoaRuntime.modulemap", ); OTHER_SWIFT_FLAGS = ( "$(inherited)", "-Xcc", + "-fmodule-map-file=$(SRCROOT)/../../Tuist/.build/checkouts/FSCalendar/FSCalendar/include/module.modulemap", + "-Xcc", "-fmodule-map-file=$(SRCROOT)/../../Tuist/.build/tuist-derived/RxCocoaRuntime/RxCocoaRuntime.modulemap", ); PRODUCT_BUNDLE_IDENTIFIER = io.tuist.ChatDemoAppTests; @@ -401,7 +409,7 @@ SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; SWIFT_COMPILATION_MODE = wholemodule; - SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; + SWIFT_OPTIMIZATION_LEVEL = "-O"; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/ChatDemoApp.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/ChatDemoApp"; @@ -416,6 +424,7 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; HEADER_SEARCH_PATHS = ( "$(inherited)", + "$(SRCROOT)/../../Tuist/.build/checkouts/FSCalendar/FSCalendar/include", "$(SRCROOT)/../../Tuist/.build/checkouts/RxSwift/Sources/RxCocoaRuntime/include", ); INFOPLIST_FILE = "Derived/InfoPlists/ChatDemoAppTests-Info.plist"; @@ -426,11 +435,14 @@ ); OTHER_CFLAGS = ( "$(inherited)", + "-fmodule-map-file=$(SRCROOT)/../../Tuist/.build/checkouts/FSCalendar/FSCalendar/include/module.modulemap", "-fmodule-map-file=$(SRCROOT)/../../Tuist/.build/tuist-derived/RxCocoaRuntime/RxCocoaRuntime.modulemap", ); OTHER_SWIFT_FLAGS = ( "$(inherited)", "-Xcc", + "-fmodule-map-file=$(SRCROOT)/../../Tuist/.build/checkouts/FSCalendar/FSCalendar/include/module.modulemap", + "-Xcc", "-fmodule-map-file=$(SRCROOT)/../../Tuist/.build/tuist-derived/RxCocoaRuntime/RxCocoaRuntime.modulemap", ); PRODUCT_BUNDLE_IDENTIFIER = io.tuist.ChatDemoAppTests; @@ -461,6 +473,7 @@ ENABLE_PREVIEWS = YES; HEADER_SEARCH_PATHS = ( "$(inherited)", + "$(SRCROOT)/../../Tuist/.build/checkouts/FSCalendar/FSCalendar/include", "$(SRCROOT)/../../Tuist/.build/checkouts/RxSwift/Sources/RxCocoaRuntime/include", ); INFOPLIST_FILE = "Derived/InfoPlists/ChatDemoApp-Info.plist"; @@ -470,11 +483,14 @@ ); OTHER_CFLAGS = ( "$(inherited)", + "-fmodule-map-file=$(SRCROOT)/../../Tuist/.build/checkouts/FSCalendar/FSCalendar/include/module.modulemap", "-fmodule-map-file=$(SRCROOT)/../../Tuist/.build/tuist-derived/RxCocoaRuntime/RxCocoaRuntime.modulemap", ); OTHER_SWIFT_FLAGS = ( "$(inherited)", "-Xcc", + "-fmodule-map-file=$(SRCROOT)/../../Tuist/.build/checkouts/FSCalendar/FSCalendar/include/module.modulemap", + "-Xcc", "-fmodule-map-file=$(SRCROOT)/../../Tuist/.build/tuist-derived/RxCocoaRuntime/RxCocoaRuntime.modulemap", ); PRODUCT_BUNDLE_IDENTIFIER = io.tuist.ChatDemoApp; @@ -484,7 +500,7 @@ SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; SWIFT_COMPILATION_MODE = wholemodule; - SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; + SWIFT_OPTIMIZATION_LEVEL = "-O"; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; }; diff --git a/DemoApps/DiaryDemoApp/DiaryDemoApp.xcodeproj/project.pbxproj b/DemoApps/DiaryDemoApp/DiaryDemoApp.xcodeproj/project.pbxproj index 5de5e67..7e1d470 100644 --- a/DemoApps/DiaryDemoApp/DiaryDemoApp.xcodeproj/project.pbxproj +++ b/DemoApps/DiaryDemoApp/DiaryDemoApp.xcodeproj/project.pbxproj @@ -390,6 +390,7 @@ ENABLE_PREVIEWS = YES; HEADER_SEARCH_PATHS = ( "$(inherited)", + "$(SRCROOT)/../../Tuist/.build/checkouts/FSCalendar/FSCalendar/include", "$(SRCROOT)/../../Tuist/.build/checkouts/RxSwift/Sources/RxCocoaRuntime/include", ); INFOPLIST_FILE = "Derived/InfoPlists/DiaryDemoApp-Info.plist"; @@ -399,11 +400,14 @@ ); OTHER_CFLAGS = ( "$(inherited)", + "-fmodule-map-file=$(SRCROOT)/../../Tuist/.build/checkouts/FSCalendar/FSCalendar/include/module.modulemap", "-fmodule-map-file=$(SRCROOT)/../../Tuist/.build/tuist-derived/RxCocoaRuntime/RxCocoaRuntime.modulemap", ); OTHER_SWIFT_FLAGS = ( "$(inherited)", "-Xcc", + "-fmodule-map-file=$(SRCROOT)/../../Tuist/.build/checkouts/FSCalendar/FSCalendar/include/module.modulemap", + "-Xcc", "-fmodule-map-file=$(SRCROOT)/../../Tuist/.build/tuist-derived/RxCocoaRuntime/RxCocoaRuntime.modulemap", ); PRODUCT_BUNDLE_IDENTIFIER = io.tuist.DiaryDemoApp; @@ -413,7 +417,7 @@ SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; SWIFT_COMPILATION_MODE = wholemodule; - SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; + SWIFT_OPTIMIZATION_LEVEL = "-O"; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; }; @@ -426,6 +430,7 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; HEADER_SEARCH_PATHS = ( "$(inherited)", + "$(SRCROOT)/../../Tuist/.build/checkouts/FSCalendar/FSCalendar/include", "$(SRCROOT)/../../Tuist/.build/checkouts/RxSwift/Sources/RxCocoaRuntime/include", ); INFOPLIST_FILE = "Derived/InfoPlists/DiaryDemoAppTests-Info.plist"; @@ -436,11 +441,14 @@ ); OTHER_CFLAGS = ( "$(inherited)", + "-fmodule-map-file=$(SRCROOT)/../../Tuist/.build/checkouts/FSCalendar/FSCalendar/include/module.modulemap", "-fmodule-map-file=$(SRCROOT)/../../Tuist/.build/tuist-derived/RxCocoaRuntime/RxCocoaRuntime.modulemap", ); OTHER_SWIFT_FLAGS = ( "$(inherited)", "-Xcc", + "-fmodule-map-file=$(SRCROOT)/../../Tuist/.build/checkouts/FSCalendar/FSCalendar/include/module.modulemap", + "-Xcc", "-fmodule-map-file=$(SRCROOT)/../../Tuist/.build/tuist-derived/RxCocoaRuntime/RxCocoaRuntime.modulemap", ); PRODUCT_BUNDLE_IDENTIFIER = io.tuist.DiaryDemoAppTests; @@ -450,7 +458,7 @@ SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; SWIFT_COMPILATION_MODE = wholemodule; - SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; + SWIFT_OPTIMIZATION_LEVEL = "-O"; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/DiaryDemoApp.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/DiaryDemoApp"; @@ -467,6 +475,7 @@ ENABLE_PREVIEWS = YES; HEADER_SEARCH_PATHS = ( "$(inherited)", + "$(SRCROOT)/../../Tuist/.build/checkouts/FSCalendar/FSCalendar/include", "$(SRCROOT)/../../Tuist/.build/checkouts/RxSwift/Sources/RxCocoaRuntime/include", ); INFOPLIST_FILE = "Derived/InfoPlists/DiaryDemoApp-Info.plist"; @@ -476,11 +485,14 @@ ); OTHER_CFLAGS = ( "$(inherited)", + "-fmodule-map-file=$(SRCROOT)/../../Tuist/.build/checkouts/FSCalendar/FSCalendar/include/module.modulemap", "-fmodule-map-file=$(SRCROOT)/../../Tuist/.build/tuist-derived/RxCocoaRuntime/RxCocoaRuntime.modulemap", ); OTHER_SWIFT_FLAGS = ( "$(inherited)", "-Xcc", + "-fmodule-map-file=$(SRCROOT)/../../Tuist/.build/checkouts/FSCalendar/FSCalendar/include/module.modulemap", + "-Xcc", "-fmodule-map-file=$(SRCROOT)/../../Tuist/.build/tuist-derived/RxCocoaRuntime/RxCocoaRuntime.modulemap", ); PRODUCT_BUNDLE_IDENTIFIER = io.tuist.DiaryDemoApp; @@ -507,6 +519,7 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; HEADER_SEARCH_PATHS = ( "$(inherited)", + "$(SRCROOT)/../../Tuist/.build/checkouts/FSCalendar/FSCalendar/include", "$(SRCROOT)/../../Tuist/.build/checkouts/RxSwift/Sources/RxCocoaRuntime/include", ); INFOPLIST_FILE = "Derived/InfoPlists/DiaryDemoAppTests-Info.plist"; @@ -517,11 +530,14 @@ ); OTHER_CFLAGS = ( "$(inherited)", + "-fmodule-map-file=$(SRCROOT)/../../Tuist/.build/checkouts/FSCalendar/FSCalendar/include/module.modulemap", "-fmodule-map-file=$(SRCROOT)/../../Tuist/.build/tuist-derived/RxCocoaRuntime/RxCocoaRuntime.modulemap", ); OTHER_SWIFT_FLAGS = ( "$(inherited)", "-Xcc", + "-fmodule-map-file=$(SRCROOT)/../../Tuist/.build/checkouts/FSCalendar/FSCalendar/include/module.modulemap", + "-Xcc", "-fmodule-map-file=$(SRCROOT)/../../Tuist/.build/tuist-derived/RxCocoaRuntime/RxCocoaRuntime.modulemap", ); PRODUCT_BUNDLE_IDENTIFIER = io.tuist.DiaryDemoAppTests; diff --git a/DemoApps/HomeDemoApp/HomeDemoApp.xcodeproj/project.pbxproj b/DemoApps/HomeDemoApp/HomeDemoApp.xcodeproj/project.pbxproj index 91c7d90..c67ac8f 100644 --- a/DemoApps/HomeDemoApp/HomeDemoApp.xcodeproj/project.pbxproj +++ b/DemoApps/HomeDemoApp/HomeDemoApp.xcodeproj/project.pbxproj @@ -276,6 +276,7 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; HEADER_SEARCH_PATHS = ( "$(inherited)", + "$(SRCROOT)/../../Tuist/.build/checkouts/FSCalendar/FSCalendar/include", "$(SRCROOT)/../../Tuist/.build/checkouts/RxSwift/Sources/RxCocoaRuntime/include", ); INFOPLIST_FILE = "Derived/InfoPlists/HomeDemoAppTests-Info.plist"; @@ -286,11 +287,14 @@ ); OTHER_CFLAGS = ( "$(inherited)", + "-fmodule-map-file=$(SRCROOT)/../../Tuist/.build/checkouts/FSCalendar/FSCalendar/include/module.modulemap", "-fmodule-map-file=$(SRCROOT)/../../Tuist/.build/tuist-derived/RxCocoaRuntime/RxCocoaRuntime.modulemap", ); OTHER_SWIFT_FLAGS = ( "$(inherited)", "-Xcc", + "-fmodule-map-file=$(SRCROOT)/../../Tuist/.build/checkouts/FSCalendar/FSCalendar/include/module.modulemap", + "-Xcc", "-fmodule-map-file=$(SRCROOT)/../../Tuist/.build/tuist-derived/RxCocoaRuntime/RxCocoaRuntime.modulemap", ); PRODUCT_BUNDLE_IDENTIFIER = io.tuist.HomeDemoAppTests; @@ -300,7 +304,7 @@ SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; SWIFT_COMPILATION_MODE = wholemodule; - SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; + SWIFT_OPTIMIZATION_LEVEL = "-O"; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/HomeDemoApp.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/HomeDemoApp"; @@ -315,6 +319,7 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; HEADER_SEARCH_PATHS = ( "$(inherited)", + "$(SRCROOT)/../../Tuist/.build/checkouts/FSCalendar/FSCalendar/include", "$(SRCROOT)/../../Tuist/.build/checkouts/RxSwift/Sources/RxCocoaRuntime/include", ); INFOPLIST_FILE = "Derived/InfoPlists/HomeDemoAppTests-Info.plist"; @@ -325,11 +330,14 @@ ); OTHER_CFLAGS = ( "$(inherited)", + "-fmodule-map-file=$(SRCROOT)/../../Tuist/.build/checkouts/FSCalendar/FSCalendar/include/module.modulemap", "-fmodule-map-file=$(SRCROOT)/../../Tuist/.build/tuist-derived/RxCocoaRuntime/RxCocoaRuntime.modulemap", ); OTHER_SWIFT_FLAGS = ( "$(inherited)", "-Xcc", + "-fmodule-map-file=$(SRCROOT)/../../Tuist/.build/checkouts/FSCalendar/FSCalendar/include/module.modulemap", + "-Xcc", "-fmodule-map-file=$(SRCROOT)/../../Tuist/.build/tuist-derived/RxCocoaRuntime/RxCocoaRuntime.modulemap", ); PRODUCT_BUNDLE_IDENTIFIER = io.tuist.HomeDemoAppTests; @@ -413,6 +421,7 @@ ENABLE_PREVIEWS = YES; HEADER_SEARCH_PATHS = ( "$(inherited)", + "$(SRCROOT)/../../Tuist/.build/checkouts/FSCalendar/FSCalendar/include", "$(SRCROOT)/../../Tuist/.build/checkouts/RxSwift/Sources/RxCocoaRuntime/include", ); INFOPLIST_FILE = "Derived/InfoPlists/HomeDemoApp-Info.plist"; @@ -422,11 +431,14 @@ ); OTHER_CFLAGS = ( "$(inherited)", + "-fmodule-map-file=$(SRCROOT)/../../Tuist/.build/checkouts/FSCalendar/FSCalendar/include/module.modulemap", "-fmodule-map-file=$(SRCROOT)/../../Tuist/.build/tuist-derived/RxCocoaRuntime/RxCocoaRuntime.modulemap", ); OTHER_SWIFT_FLAGS = ( "$(inherited)", "-Xcc", + "-fmodule-map-file=$(SRCROOT)/../../Tuist/.build/checkouts/FSCalendar/FSCalendar/include/module.modulemap", + "-Xcc", "-fmodule-map-file=$(SRCROOT)/../../Tuist/.build/tuist-derived/RxCocoaRuntime/RxCocoaRuntime.modulemap", ); PRODUCT_BUNDLE_IDENTIFIER = io.tuist.HomeDemoApp; @@ -436,7 +448,7 @@ SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; SWIFT_COMPILATION_MODE = wholemodule; - SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; + SWIFT_OPTIMIZATION_LEVEL = "-O"; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; }; @@ -451,6 +463,7 @@ ENABLE_PREVIEWS = YES; HEADER_SEARCH_PATHS = ( "$(inherited)", + "$(SRCROOT)/../../Tuist/.build/checkouts/FSCalendar/FSCalendar/include", "$(SRCROOT)/../../Tuist/.build/checkouts/RxSwift/Sources/RxCocoaRuntime/include", ); INFOPLIST_FILE = "Derived/InfoPlists/HomeDemoApp-Info.plist"; @@ -460,11 +473,14 @@ ); OTHER_CFLAGS = ( "$(inherited)", + "-fmodule-map-file=$(SRCROOT)/../../Tuist/.build/checkouts/FSCalendar/FSCalendar/include/module.modulemap", "-fmodule-map-file=$(SRCROOT)/../../Tuist/.build/tuist-derived/RxCocoaRuntime/RxCocoaRuntime.modulemap", ); OTHER_SWIFT_FLAGS = ( "$(inherited)", "-Xcc", + "-fmodule-map-file=$(SRCROOT)/../../Tuist/.build/checkouts/FSCalendar/FSCalendar/include/module.modulemap", + "-Xcc", "-fmodule-map-file=$(SRCROOT)/../../Tuist/.build/tuist-derived/RxCocoaRuntime/RxCocoaRuntime.modulemap", ); PRODUCT_BUNDLE_IDENTIFIER = io.tuist.HomeDemoApp; diff --git a/DemoApps/MyPageDemoApp/MyPageDemoApp.xcodeproj/project.pbxproj b/DemoApps/MyPageDemoApp/MyPageDemoApp.xcodeproj/project.pbxproj index 546e3ec..c479ae9 100644 --- a/DemoApps/MyPageDemoApp/MyPageDemoApp.xcodeproj/project.pbxproj +++ b/DemoApps/MyPageDemoApp/MyPageDemoApp.xcodeproj/project.pbxproj @@ -278,6 +278,7 @@ ENABLE_PREVIEWS = YES; HEADER_SEARCH_PATHS = ( "$(inherited)", + "$(SRCROOT)/../../Tuist/.build/checkouts/FSCalendar/FSCalendar/include", "$(SRCROOT)/../../Tuist/.build/checkouts/RxSwift/Sources/RxCocoaRuntime/include", ); INFOPLIST_FILE = "Derived/InfoPlists/MyPageDemoApp-Info.plist"; @@ -287,11 +288,14 @@ ); OTHER_CFLAGS = ( "$(inherited)", + "-fmodule-map-file=$(SRCROOT)/../../Tuist/.build/checkouts/FSCalendar/FSCalendar/include/module.modulemap", "-fmodule-map-file=$(SRCROOT)/../../Tuist/.build/tuist-derived/RxCocoaRuntime/RxCocoaRuntime.modulemap", ); OTHER_SWIFT_FLAGS = ( "$(inherited)", "-Xcc", + "-fmodule-map-file=$(SRCROOT)/../../Tuist/.build/checkouts/FSCalendar/FSCalendar/include/module.modulemap", + "-Xcc", "-fmodule-map-file=$(SRCROOT)/../../Tuist/.build/tuist-derived/RxCocoaRuntime/RxCocoaRuntime.modulemap", ); PRODUCT_BUNDLE_IDENTIFIER = io.tuist.MyPageDemoApp; @@ -301,7 +305,7 @@ SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; SWIFT_COMPILATION_MODE = wholemodule; - SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; + SWIFT_OPTIMIZATION_LEVEL = "-O"; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; }; @@ -316,6 +320,7 @@ ENABLE_PREVIEWS = YES; HEADER_SEARCH_PATHS = ( "$(inherited)", + "$(SRCROOT)/../../Tuist/.build/checkouts/FSCalendar/FSCalendar/include", "$(SRCROOT)/../../Tuist/.build/checkouts/RxSwift/Sources/RxCocoaRuntime/include", ); INFOPLIST_FILE = "Derived/InfoPlists/MyPageDemoApp-Info.plist"; @@ -325,11 +330,14 @@ ); OTHER_CFLAGS = ( "$(inherited)", + "-fmodule-map-file=$(SRCROOT)/../../Tuist/.build/checkouts/FSCalendar/FSCalendar/include/module.modulemap", "-fmodule-map-file=$(SRCROOT)/../../Tuist/.build/tuist-derived/RxCocoaRuntime/RxCocoaRuntime.modulemap", ); OTHER_SWIFT_FLAGS = ( "$(inherited)", "-Xcc", + "-fmodule-map-file=$(SRCROOT)/../../Tuist/.build/checkouts/FSCalendar/FSCalendar/include/module.modulemap", + "-Xcc", "-fmodule-map-file=$(SRCROOT)/../../Tuist/.build/tuist-derived/RxCocoaRuntime/RxCocoaRuntime.modulemap", ); PRODUCT_BUNDLE_IDENTIFIER = io.tuist.MyPageDemoApp; @@ -356,6 +364,7 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; HEADER_SEARCH_PATHS = ( "$(inherited)", + "$(SRCROOT)/../../Tuist/.build/checkouts/FSCalendar/FSCalendar/include", "$(SRCROOT)/../../Tuist/.build/checkouts/RxSwift/Sources/RxCocoaRuntime/include", ); INFOPLIST_FILE = "Derived/InfoPlists/MyPageDemoAppTests-Info.plist"; @@ -366,11 +375,14 @@ ); OTHER_CFLAGS = ( "$(inherited)", + "-fmodule-map-file=$(SRCROOT)/../../Tuist/.build/checkouts/FSCalendar/FSCalendar/include/module.modulemap", "-fmodule-map-file=$(SRCROOT)/../../Tuist/.build/tuist-derived/RxCocoaRuntime/RxCocoaRuntime.modulemap", ); OTHER_SWIFT_FLAGS = ( "$(inherited)", "-Xcc", + "-fmodule-map-file=$(SRCROOT)/../../Tuist/.build/checkouts/FSCalendar/FSCalendar/include/module.modulemap", + "-Xcc", "-fmodule-map-file=$(SRCROOT)/../../Tuist/.build/tuist-derived/RxCocoaRuntime/RxCocoaRuntime.modulemap", ); PRODUCT_BUNDLE_IDENTIFIER = io.tuist.MyPageDemoAppTests; @@ -380,7 +392,7 @@ SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; SWIFT_COMPILATION_MODE = wholemodule; - SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; + SWIFT_OPTIMIZATION_LEVEL = "-O"; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/MyPageDemoApp.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/MyPageDemoApp"; @@ -507,6 +519,7 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; HEADER_SEARCH_PATHS = ( "$(inherited)", + "$(SRCROOT)/../../Tuist/.build/checkouts/FSCalendar/FSCalendar/include", "$(SRCROOT)/../../Tuist/.build/checkouts/RxSwift/Sources/RxCocoaRuntime/include", ); INFOPLIST_FILE = "Derived/InfoPlists/MyPageDemoAppTests-Info.plist"; @@ -517,11 +530,14 @@ ); OTHER_CFLAGS = ( "$(inherited)", + "-fmodule-map-file=$(SRCROOT)/../../Tuist/.build/checkouts/FSCalendar/FSCalendar/include/module.modulemap", "-fmodule-map-file=$(SRCROOT)/../../Tuist/.build/tuist-derived/RxCocoaRuntime/RxCocoaRuntime.modulemap", ); OTHER_SWIFT_FLAGS = ( "$(inherited)", "-Xcc", + "-fmodule-map-file=$(SRCROOT)/../../Tuist/.build/checkouts/FSCalendar/FSCalendar/include/module.modulemap", + "-Xcc", "-fmodule-map-file=$(SRCROOT)/../../Tuist/.build/tuist-derived/RxCocoaRuntime/RxCocoaRuntime.modulemap", ); PRODUCT_BUNDLE_IDENTIFIER = io.tuist.MyPageDemoAppTests; diff --git a/DemoApps/StatsDemoApp/StatsDemoApp.xcodeproj/project.pbxproj b/DemoApps/StatsDemoApp/StatsDemoApp.xcodeproj/project.pbxproj index 11cc87b..dde62de 100644 --- a/DemoApps/StatsDemoApp/StatsDemoApp.xcodeproj/project.pbxproj +++ b/DemoApps/StatsDemoApp/StatsDemoApp.xcodeproj/project.pbxproj @@ -335,6 +335,7 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; HEADER_SEARCH_PATHS = ( "$(inherited)", + "$(SRCROOT)/../../Tuist/.build/checkouts/FSCalendar/FSCalendar/include", "$(SRCROOT)/../../Tuist/.build/checkouts/RxSwift/Sources/RxCocoaRuntime/include", ); INFOPLIST_FILE = "Derived/InfoPlists/StatsDemoAppTests-Info.plist"; @@ -345,11 +346,14 @@ ); OTHER_CFLAGS = ( "$(inherited)", + "-fmodule-map-file=$(SRCROOT)/../../Tuist/.build/checkouts/FSCalendar/FSCalendar/include/module.modulemap", "-fmodule-map-file=$(SRCROOT)/../../Tuist/.build/tuist-derived/RxCocoaRuntime/RxCocoaRuntime.modulemap", ); OTHER_SWIFT_FLAGS = ( "$(inherited)", "-Xcc", + "-fmodule-map-file=$(SRCROOT)/../../Tuist/.build/checkouts/FSCalendar/FSCalendar/include/module.modulemap", + "-Xcc", "-fmodule-map-file=$(SRCROOT)/../../Tuist/.build/tuist-derived/RxCocoaRuntime/RxCocoaRuntime.modulemap", ); PRODUCT_BUNDLE_IDENTIFIER = io.tuist.StatsDemoAppTests; @@ -359,7 +363,7 @@ SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; SWIFT_COMPILATION_MODE = wholemodule; - SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; + SWIFT_OPTIMIZATION_LEVEL = "-O"; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/StatsDemoApp.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/StatsDemoApp"; @@ -429,6 +433,7 @@ ENABLE_PREVIEWS = YES; HEADER_SEARCH_PATHS = ( "$(inherited)", + "$(SRCROOT)/../../Tuist/.build/checkouts/FSCalendar/FSCalendar/include", "$(SRCROOT)/../../Tuist/.build/checkouts/RxSwift/Sources/RxCocoaRuntime/include", ); INFOPLIST_FILE = "Derived/InfoPlists/StatsDemoApp-Info.plist"; @@ -438,11 +443,14 @@ ); OTHER_CFLAGS = ( "$(inherited)", + "-fmodule-map-file=$(SRCROOT)/../../Tuist/.build/checkouts/FSCalendar/FSCalendar/include/module.modulemap", "-fmodule-map-file=$(SRCROOT)/../../Tuist/.build/tuist-derived/RxCocoaRuntime/RxCocoaRuntime.modulemap", ); OTHER_SWIFT_FLAGS = ( "$(inherited)", "-Xcc", + "-fmodule-map-file=$(SRCROOT)/../../Tuist/.build/checkouts/FSCalendar/FSCalendar/include/module.modulemap", + "-Xcc", "-fmodule-map-file=$(SRCROOT)/../../Tuist/.build/tuist-derived/RxCocoaRuntime/RxCocoaRuntime.modulemap", ); PRODUCT_BUNDLE_IDENTIFIER = io.tuist.StatsDemoApp; @@ -452,7 +460,7 @@ SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; SWIFT_COMPILATION_MODE = wholemodule; - SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; + SWIFT_OPTIMIZATION_LEVEL = "-O"; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; }; @@ -465,6 +473,7 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; HEADER_SEARCH_PATHS = ( "$(inherited)", + "$(SRCROOT)/../../Tuist/.build/checkouts/FSCalendar/FSCalendar/include", "$(SRCROOT)/../../Tuist/.build/checkouts/RxSwift/Sources/RxCocoaRuntime/include", ); INFOPLIST_FILE = "Derived/InfoPlists/StatsDemoAppTests-Info.plist"; @@ -475,11 +484,14 @@ ); OTHER_CFLAGS = ( "$(inherited)", + "-fmodule-map-file=$(SRCROOT)/../../Tuist/.build/checkouts/FSCalendar/FSCalendar/include/module.modulemap", "-fmodule-map-file=$(SRCROOT)/../../Tuist/.build/tuist-derived/RxCocoaRuntime/RxCocoaRuntime.modulemap", ); OTHER_SWIFT_FLAGS = ( "$(inherited)", "-Xcc", + "-fmodule-map-file=$(SRCROOT)/../../Tuist/.build/checkouts/FSCalendar/FSCalendar/include/module.modulemap", + "-Xcc", "-fmodule-map-file=$(SRCROOT)/../../Tuist/.build/tuist-derived/RxCocoaRuntime/RxCocoaRuntime.modulemap", ); PRODUCT_BUNDLE_IDENTIFIER = io.tuist.StatsDemoAppTests; @@ -510,6 +522,7 @@ ENABLE_PREVIEWS = YES; HEADER_SEARCH_PATHS = ( "$(inherited)", + "$(SRCROOT)/../../Tuist/.build/checkouts/FSCalendar/FSCalendar/include", "$(SRCROOT)/../../Tuist/.build/checkouts/RxSwift/Sources/RxCocoaRuntime/include", ); INFOPLIST_FILE = "Derived/InfoPlists/StatsDemoApp-Info.plist"; @@ -519,11 +532,14 @@ ); OTHER_CFLAGS = ( "$(inherited)", + "-fmodule-map-file=$(SRCROOT)/../../Tuist/.build/checkouts/FSCalendar/FSCalendar/include/module.modulemap", "-fmodule-map-file=$(SRCROOT)/../../Tuist/.build/tuist-derived/RxCocoaRuntime/RxCocoaRuntime.modulemap", ); OTHER_SWIFT_FLAGS = ( "$(inherited)", "-Xcc", + "-fmodule-map-file=$(SRCROOT)/../../Tuist/.build/checkouts/FSCalendar/FSCalendar/include/module.modulemap", + "-Xcc", "-fmodule-map-file=$(SRCROOT)/../../Tuist/.build/tuist-derived/RxCocoaRuntime/RxCocoaRuntime.modulemap", ); PRODUCT_BUNDLE_IDENTIFIER = io.tuist.StatsDemoApp; diff --git a/Projects/Chat/Chat.xcodeproj/project.pbxproj b/Projects/Chat/Chat.xcodeproj/project.pbxproj index 3848aaa..e0556a1 100644 --- a/Projects/Chat/Chat.xcodeproj/project.pbxproj +++ b/Projects/Chat/Chat.xcodeproj/project.pbxproj @@ -3,18 +3,18 @@ archiveVersion = 1; classes = { }; - objectVersion = 56; + objectVersion = 55; objects = { /* Begin PBXBuildFile section */ 11E1631C9C29E4197C2782CB /* ChatViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E083003E4769EA2B259F4BE8 /* ChatViewController.swift */; }; + 11FB77FA2F86CE64816D448C /* ChatAnalysisViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD38136DBF1DC16E4E5C1149 /* ChatAnalysisViewController.swift */; }; 3B4198F882286B277F8A91E1 /* Then.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5E359E9093579B130E0EDD53 /* Then.framework */; }; 7F3F4DE30EE902DEABC6040E /* ChatViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5AAFFC509053CC99EAA3B277 /* ChatViewModel.swift */; }; 9179FB9B1C2AEDCB7A3CFC7E /* ChatCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 588CFD307FD55B64676DA657 /* ChatCoordinator.swift */; }; 91C973D65FBDD1ABF65A1A2A /* Domain.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F910F30DEBC80ABF5CC5A6F9 /* Domain.framework */; }; - 9516ED482E7076F800F548A1 /* ChatAnalysisViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9516ED472E7076E900F548A1 /* ChatAnalysisViewController.swift */; }; - 9516ED4F2E708CE100F548A1 /* ChatMainViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9516ED4E2E708CD900F548A1 /* ChatMainViewController.swift */; }; - 956C4D722E7690C600E32F93 /* ChatDetailViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 956C4D712E7690BF00E32F93 /* ChatDetailViewController.swift */; }; + 9D4DCAC5131580FCDFB46C37 /* ChatMainViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12F3020541ADB39BD0E8D703 /* ChatMainViewController.swift */; }; + A1775C0ED2D81A830AFBC418 /* ChatDetailViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 120F4BAB810472885244650D /* ChatDetailViewController.swift */; }; B6F14AC32696F30284073B2F /* Chat.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = EEE28C25EAD3F5DC9E76FFAC /* Chat.framework */; }; B89B886F288E5BDECC82BA50 /* Common.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 64BD6CA2A2FDA5A5D9C4A87D /* Common.framework */; }; E096380D552BAA6326ED8397 /* CommonUI.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1CB61D7206C15B58E35E2DEB /* CommonUI.framework */; }; @@ -57,21 +57,21 @@ /* Begin PBXFileReference section */ 082A769CEB29C2CF1616DE7A /* ChatTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ChatTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + 120F4BAB810472885244650D /* ChatDetailViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatDetailViewController.swift; sourceTree = ""; }; + 12F3020541ADB39BD0E8D703 /* ChatMainViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatMainViewController.swift; sourceTree = ""; }; 1C9D9ED19FA740929CF59EFD /* ChatTests-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = "ChatTests-Info.plist"; sourceTree = ""; }; 1CB61D7206C15B58E35E2DEB /* CommonUI.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = CommonUI.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 588CFD307FD55B64676DA657 /* ChatCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatCoordinator.swift; sourceTree = ""; }; 5AAFFC509053CC99EAA3B277 /* ChatViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatViewModel.swift; sourceTree = ""; }; 5E359E9093579B130E0EDD53 /* Then.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Then.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 64BD6CA2A2FDA5A5D9C4A87D /* Common.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Common.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 9516ED472E7076E900F548A1 /* ChatAnalysisViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatAnalysisViewController.swift; sourceTree = ""; }; - 9516ED4E2E708CD900F548A1 /* ChatMainViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatMainViewController.swift; sourceTree = ""; }; - 956C4D712E7690BF00E32F93 /* ChatDetailViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatDetailViewController.swift; sourceTree = ""; }; BC90A71D97E9F538AACFD586 /* SnapKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SnapKit.framework; sourceTree = BUILT_PRODUCTS_DIR; }; C8A6CF81DAC793585959F31D /* Chat-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = "Chat-Info.plist"; sourceTree = ""; }; E05B48A08FE1A700DE3FEE63 /* RxSwift.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = RxSwift.framework; sourceTree = BUILT_PRODUCTS_DIR; }; E083003E4769EA2B259F4BE8 /* ChatViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatViewController.swift; sourceTree = ""; }; EEE28C25EAD3F5DC9E76FFAC /* Chat.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Chat.framework; sourceTree = BUILT_PRODUCTS_DIR; }; F910F30DEBC80ABF5CC5A6F9 /* Domain.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Domain.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + FD38136DBF1DC16E4E5C1149 /* ChatAnalysisViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatAnalysisViewController.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -160,10 +160,10 @@ B076E5D7F9914D43D18020A7 /* View */ = { isa = PBXGroup; children = ( - 9516ED4E2E708CD900F548A1 /* ChatMainViewController.swift */, + FD38136DBF1DC16E4E5C1149 /* ChatAnalysisViewController.swift */, + 120F4BAB810472885244650D /* ChatDetailViewController.swift */, + 12F3020541ADB39BD0E8D703 /* ChatMainViewController.swift */, E083003E4769EA2B259F4BE8 /* ChatViewController.swift */, - 9516ED472E7076E900F548A1 /* ChatAnalysisViewController.swift */, - 956C4D712E7690BF00E32F93 /* ChatDetailViewController.swift */, ); path = View; sourceTree = ""; @@ -236,6 +236,8 @@ isa = PBXProject; attributes = { BuildIndependentTargetsInParallel = YES; + TargetAttributes = { + }; }; buildConfigurationList = 5BF2D8A0F20A5563CE9A6AEB /* Build configuration list for PBXProject "Chat" */; compatibilityVersion = "Xcode 14.0"; @@ -285,12 +287,12 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 9516ED4F2E708CE100F548A1 /* ChatMainViewController.swift in Sources */, 9179FB9B1C2AEDCB7A3CFC7E /* ChatCoordinator.swift in Sources */, + 11FB77FA2F86CE64816D448C /* ChatAnalysisViewController.swift in Sources */, + A1775C0ED2D81A830AFBC418 /* ChatDetailViewController.swift in Sources */, + 9D4DCAC5131580FCDFB46C37 /* ChatMainViewController.swift in Sources */, 11E1631C9C29E4197C2782CB /* ChatViewController.swift in Sources */, 7F3F4DE30EE902DEABC6040E /* ChatViewModel.swift in Sources */, - 9516ED482E7076F800F548A1 /* ChatAnalysisViewController.swift in Sources */, - 956C4D722E7690C600E32F93 /* ChatDetailViewController.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -312,6 +314,7 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; HEADER_SEARCH_PATHS = ( "$(inherited)", + "$(SRCROOT)/../../Tuist/.build/checkouts/FSCalendar/FSCalendar/include", "$(SRCROOT)/../../Tuist/.build/checkouts/RxSwift/Sources/RxCocoaRuntime/include", ); INFOPLIST_FILE = "Derived/InfoPlists/ChatTests-Info.plist"; @@ -322,16 +325,26 @@ ); OTHER_CFLAGS = ( "$(inherited)", + "-fmodule-map-file=$(SRCROOT)/../../Tuist/.build/checkouts/FSCalendar/FSCalendar/include/module.modulemap", + "-fmodule-map-file=$(SRCROOT)/../../Tuist/.build/tuist-derived/RxCocoaRuntime/RxCocoaRuntime.modulemap", + ); + OTHER_SWIFT_FLAGS = ( + "$(inherited)", + "-Xcc", + "-fmodule-map-file=$(SRCROOT)/../../Tuist/.build/checkouts/FSCalendar/FSCalendar/include/module.modulemap", + "-Xcc", "-fmodule-map-file=$(SRCROOT)/../../Tuist/.build/tuist-derived/RxCocoaRuntime/RxCocoaRuntime.modulemap", ); - OTHER_SWIFT_FLAGS = "$(inherited) -Xcc -fmodule-map-file=$(SRCROOT)/../../Tuist/.build/tuist-derived/RxCocoaRuntime/RxCocoaRuntime.modulemap"; PRODUCT_BUNDLE_IDENTIFIER = io.tuist.ChatTests; PRODUCT_NAME = ChatTests; SDKROOT = iphoneos; SUPPORTS_MACCATALYST = NO; SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; - SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) DEBUG"; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = ( + "$(inherited)", + DEBUG, + ); SWIFT_COMPILATION_MODE = singlefile; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 5.0; @@ -403,6 +416,7 @@ DYLIB_INSTALL_NAME_BASE = "@rpath"; HEADER_SEARCH_PATHS = ( "$(inherited)", + "$(SRCROOT)/../../Tuist/.build/checkouts/FSCalendar/FSCalendar/include", "$(SRCROOT)/../../Tuist/.build/checkouts/RxSwift/Sources/RxCocoaRuntime/include", ); INFOPLIST_FILE = "Derived/InfoPlists/Chat-Info.plist"; @@ -414,9 +428,16 @@ ); OTHER_CFLAGS = ( "$(inherited)", + "-fmodule-map-file=$(SRCROOT)/../../Tuist/.build/checkouts/FSCalendar/FSCalendar/include/module.modulemap", + "-fmodule-map-file=$(SRCROOT)/../../Tuist/.build/tuist-derived/RxCocoaRuntime/RxCocoaRuntime.modulemap", + ); + OTHER_SWIFT_FLAGS = ( + "$(inherited)", + "-Xcc", + "-fmodule-map-file=$(SRCROOT)/../../Tuist/.build/checkouts/FSCalendar/FSCalendar/include/module.modulemap", + "-Xcc", "-fmodule-map-file=$(SRCROOT)/../../Tuist/.build/tuist-derived/RxCocoaRuntime/RxCocoaRuntime.modulemap", ); - OTHER_SWIFT_FLAGS = "$(inherited) -Xcc -fmodule-map-file=$(SRCROOT)/../../Tuist/.build/tuist-derived/RxCocoaRuntime/RxCocoaRuntime.modulemap"; PRODUCT_BUNDLE_IDENTIFIER = io.tuist.Chat; PRODUCT_NAME = Chat; SDKROOT = iphoneos; @@ -425,7 +446,7 @@ SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; SWIFT_COMPILATION_MODE = wholemodule; - SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; + SWIFT_OPTIMIZATION_LEVEL = "-O"; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; VERSIONING_SYSTEM = "apple-generic"; @@ -498,6 +519,7 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; HEADER_SEARCH_PATHS = ( "$(inherited)", + "$(SRCROOT)/../../Tuist/.build/checkouts/FSCalendar/FSCalendar/include", "$(SRCROOT)/../../Tuist/.build/checkouts/RxSwift/Sources/RxCocoaRuntime/include", ); INFOPLIST_FILE = "Derived/InfoPlists/ChatTests-Info.plist"; @@ -508,9 +530,16 @@ ); OTHER_CFLAGS = ( "$(inherited)", + "-fmodule-map-file=$(SRCROOT)/../../Tuist/.build/checkouts/FSCalendar/FSCalendar/include/module.modulemap", + "-fmodule-map-file=$(SRCROOT)/../../Tuist/.build/tuist-derived/RxCocoaRuntime/RxCocoaRuntime.modulemap", + ); + OTHER_SWIFT_FLAGS = ( + "$(inherited)", + "-Xcc", + "-fmodule-map-file=$(SRCROOT)/../../Tuist/.build/checkouts/FSCalendar/FSCalendar/include/module.modulemap", + "-Xcc", "-fmodule-map-file=$(SRCROOT)/../../Tuist/.build/tuist-derived/RxCocoaRuntime/RxCocoaRuntime.modulemap", ); - OTHER_SWIFT_FLAGS = "$(inherited) -Xcc -fmodule-map-file=$(SRCROOT)/../../Tuist/.build/tuist-derived/RxCocoaRuntime/RxCocoaRuntime.modulemap"; PRODUCT_BUNDLE_IDENTIFIER = io.tuist.ChatTests; PRODUCT_NAME = ChatTests; SDKROOT = iphoneos; @@ -518,7 +547,7 @@ SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; SWIFT_COMPILATION_MODE = wholemodule; - SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; + SWIFT_OPTIMIZATION_LEVEL = "-O"; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; }; @@ -535,6 +564,7 @@ DYLIB_INSTALL_NAME_BASE = "@rpath"; HEADER_SEARCH_PATHS = ( "$(inherited)", + "$(SRCROOT)/../../Tuist/.build/checkouts/FSCalendar/FSCalendar/include", "$(SRCROOT)/../../Tuist/.build/checkouts/RxSwift/Sources/RxCocoaRuntime/include", ); INFOPLIST_FILE = "Derived/InfoPlists/Chat-Info.plist"; @@ -546,9 +576,16 @@ ); OTHER_CFLAGS = ( "$(inherited)", + "-fmodule-map-file=$(SRCROOT)/../../Tuist/.build/checkouts/FSCalendar/FSCalendar/include/module.modulemap", + "-fmodule-map-file=$(SRCROOT)/../../Tuist/.build/tuist-derived/RxCocoaRuntime/RxCocoaRuntime.modulemap", + ); + OTHER_SWIFT_FLAGS = ( + "$(inherited)", + "-Xcc", + "-fmodule-map-file=$(SRCROOT)/../../Tuist/.build/checkouts/FSCalendar/FSCalendar/include/module.modulemap", + "-Xcc", "-fmodule-map-file=$(SRCROOT)/../../Tuist/.build/tuist-derived/RxCocoaRuntime/RxCocoaRuntime.modulemap", ); - OTHER_SWIFT_FLAGS = "$(inherited) -Xcc -fmodule-map-file=$(SRCROOT)/../../Tuist/.build/tuist-derived/RxCocoaRuntime/RxCocoaRuntime.modulemap"; PRODUCT_BUNDLE_IDENTIFIER = io.tuist.Chat; PRODUCT_NAME = Chat; SDKROOT = iphoneos; @@ -556,7 +593,10 @@ SUPPORTS_MACCATALYST = NO; SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; - SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) DEBUG"; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = ( + "$(inherited)", + DEBUG, + ); SWIFT_COMPILATION_MODE = singlefile; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 5.0; diff --git a/Projects/Chat/Sources/View/ChatAnalysisViewController.swift b/Projects/Chat/Sources/View/ChatAnalysisViewController.swift index f9b8cbb..7c1e45f 100644 --- a/Projects/Chat/Sources/View/ChatAnalysisViewController.swift +++ b/Projects/Chat/Sources/View/ChatAnalysisViewController.swift @@ -81,7 +81,6 @@ public class ChatAnalysisViewController: BaseViewController { let lmAlert = LMAlert(title: "저장하지 않은 대화는 사라집니다.\n그래도 나가시겠습니까?") lmAlert.setCancelAction { - // 아니요 버튼 - 아무것도 하지 않음 } lmAlert.setConfirmAction { [weak self] in diff --git a/Projects/Chat/Sources/ViewModel/ChatViewModel.swift b/Projects/Chat/Sources/ViewModel/ChatViewModel.swift index b476ab4..ca98242 100644 --- a/Projects/Chat/Sources/ViewModel/ChatViewModel.swift +++ b/Projects/Chat/Sources/ViewModel/ChatViewModel.swift @@ -9,8 +9,12 @@ import Domain import RxSwift protocol ChatViewModelProtocol { + func getChatList() func startTextChat() func sendMessage(content: String) + func postChatAnalysis() + func deleteChat() + func getChatDetail(chatRoomId: Int) } public class ChatViewModel: ChatViewModelProtocol { diff --git a/Projects/Common/Common.xcodeproj/project.pbxproj b/Projects/Common/Common.xcodeproj/project.pbxproj index 31deee3..e1d1bc0 100644 --- a/Projects/Common/Common.xcodeproj/project.pbxproj +++ b/Projects/Common/Common.xcodeproj/project.pbxproj @@ -186,7 +186,7 @@ SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; SWIFT_COMPILATION_MODE = wholemodule; - SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; + SWIFT_OPTIMIZATION_LEVEL = "-O"; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; VERSIONING_SYSTEM = "apple-generic"; diff --git a/Projects/CommonUI/CommonUI.xcodeproj/project.pbxproj b/Projects/CommonUI/CommonUI.xcodeproj/project.pbxproj index 5e9dac9..0beaa11 100644 --- a/Projects/CommonUI/CommonUI.xcodeproj/project.pbxproj +++ b/Projects/CommonUI/CommonUI.xcodeproj/project.pbxproj @@ -7,44 +7,54 @@ objects = { /* Begin PBXBuildFile section */ + 0E81854D9C2CC55C24DEA5A8 /* SignUpView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 73E4522EABE13359D77758CA /* SignUpView.swift */; }; + 21B9DB651F4D3F7DFB5CE055 /* LMTextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = 22C43ED73377A3ADBF7DF855 /* LMTextField.swift */; }; + 318A2464888400F05D38B7E5 /* DiaryView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D81F42DF8103F21916D13101 /* DiaryView.swift */; }; + 3DE37C9DF3C45F1BE84AB98E /* ChatListCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD604E204E1BFC232B5C48AC /* ChatListCell.swift */; }; 416C58AEE5E4491991982CFF /* HomeProgressView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7CE59FBFD1AAA1B5E7B45FD5 /* HomeProgressView.swift */; }; + 43D87B408B5AEBF565104F7E /* SignInView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE79D12719B54383633DF73 /* SignInView.swift */; }; + 451E4F690605A30900E088FE /* ChatView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54D77EBBB5DC07D000BA651F /* ChatView.swift */; }; 473C96577A92DD6F71241A76 /* MyPageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9266801EFBA6178C5B70C641 /* MyPageView.swift */; }; 4782805C566E0871101C2B60 /* RxSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F1321D979BCBEAF892D8C0C8 /* RxSwift.framework */; }; 4950E02CD2F0E415D618CD16 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 5026821CC7DCD8E25C99E1BE /* Images.xcassets */; }; 4A71A59F40DC619E678B1CAD /* RxCocoaRuntime.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CA4151AAF9C1E241C733FF76 /* RxCocoaRuntime.framework */; }; 537F80B2F39FD73F6F78F9B2 /* HomeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 47AA88AB7E000E21ED0BD7D8 /* HomeView.swift */; }; + 568C33E681D9B58B71273564 /* DiaryTodayView.swift in Sources */ = {isa = PBXBuildFile; fileRef = ACC4F396C35AD4C9F54E4BF6 /* DiaryTodayView.swift */; }; 592FAEA836FD6B4B3F466D72 /* QuizCompleteAlertView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1591A892B06D329BE11253BC /* QuizCompleteAlertView.swift */; }; 65762CE867888754D56BA2CB /* OptionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BACC7259FC0C14CB352A4E6B /* OptionView.swift */; }; 65B3402D15A9F06B3E88CE19 /* QuizView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 70367F4314114F624CA9A94B /* QuizView.swift */; }; 70139D721530B3262C44ABC1 /* AnswerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D089568D67BA41DD38CCFD6B /* AnswerView.swift */; }; + 736D1C5EB53FB204B030C397 /* DiaryAddView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 179BF0E6EE3B59F510B8AF40 /* DiaryAddView.swift */; }; 7D319882A302F75CCE46A48C /* HomeQuizView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 39A77FC9A0E2D7F62CA16E90 /* HomeQuizView.swift */; }; 7DC59B80630854028C7C80F4 /* TuistBundle+CommonUI.swift in Sources */ = {isa = PBXBuildFile; fileRef = 94EF204F3CD6F86C6167C300 /* TuistBundle+CommonUI.swift */; }; - 950A0D4F2E5AADB500C07CF2 /* SignUpView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 950A0D4E2E5AADA700C07CF2 /* SignUpView.swift */; }; - 950A0D512E5AADC400C07CF2 /* SignInView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 950A0D502E5AADC100C07CF2 /* SignInView.swift */; }; - 950A0D562E5C29D000C07CF2 /* LMTextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = 950A0D552E5C29CC00C07CF2 /* LMTextField.swift */; }; - 950A0D602E5C3C7000C07CF2 /* LMButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 950A0D5F2E5C3C6D00C07CF2 /* LMButton.swift */; }; - 950A0D622E5C562700C07CF2 /* LMInputField.swift in Sources */ = {isa = PBXBuildFile; fileRef = 950A0D612E5C561400C07CF2 /* LMInputField.swift */; }; - 950A0D962E605CEA00C07CF2 /* UIStackView+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 950A0D952E605CE300C07CF2 /* UIStackView+Extension.swift */; }; - 9516ED4A2E7076FF00F548A1 /* ChatAnalysisView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9516ED492E7076FB00F548A1 /* ChatAnalysisView.swift */; }; - 9516ED4D2E707ACF00F548A1 /* ChatAnalysisLoadingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9516ED4C2E707AC900F548A1 /* ChatAnalysisLoadingView.swift */; }; - 9516ED512E708CF700F548A1 /* ChatMainView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9516ED502E708CF200F548A1 /* ChatMainView.swift */; }; - 9516ED532E709BA600F548A1 /* ChatListCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9516ED522E709BA300F548A1 /* ChatListCell.swift */; }; - 956C4D702E767F8800E32F93 /* LMAlert.swift in Sources */ = {isa = PBXBuildFile; fileRef = 956C4D6F2E767F8300E32F93 /* LMAlert.swift */; }; - 956C4D742E7690EA00E32F93 /* ChatDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 956C4D732E7690E600E32F93 /* ChatDetailView.swift */; }; + 7DD1D21C569B040F1AB7067B /* DiaryResultView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7EEBE047F5F8EC16F0985204 /* DiaryResultView.swift */; }; + 8E0E6BDEA9574116ECBD55E4 /* ChatAnalysisView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 963B9323B265D5683B336EF2 /* ChatAnalysisView.swift */; }; + 9532C7A92E786B3C00B4BADE /* DiaryLoadingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9532C7A82E786B3800B4BADE /* DiaryLoadingView.swift */; }; + 956C4D852E76F05000E32F93 /* CalendarView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 956C4D842E76F04A00E32F93 /* CalendarView.swift */; }; + 956C4D892E76F0F500E32F93 /* DividerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 956C4D882E76F0F400E32F93 /* DividerView.swift */; }; + 95C68FAB2E7863210017D28B /* DateFormatter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 95C68FAA2E78631D0017D28B /* DateFormatter.swift */; }; + 95EDE8D52E7E60CA0091ED75 /* DiaryDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 95EDE8D42E7E60C50091ED75 /* DiaryDetailView.swift */; }; + 96EC2CC574B6ED29C83445A2 /* LMButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 283B461760B2611C637CCD35 /* LMButton.swift */; }; + A08AEF6AF6E193802D530204 /* LMAlert.swift in Sources */ = {isa = PBXBuildFile; fileRef = 195B7F96DD5F38D1EB470427 /* LMAlert.swift */; }; + A8D633A6C67CC42F3E7017EA /* LMInputField.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4D915D93710901915F0E1B11 /* LMInputField.swift */; }; + B9F3C0F9AA675E70FB8ED969 /* UIStackView+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF3F34507415EC124AF9DCE2 /* UIStackView+Extension.swift */; }; BAD8B768F782046D4AA1C073 /* RxCocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F3DE048BEDCB92B48F401061 /* RxCocoa.framework */; }; BE81B1F3E60D37D75A058D2B /* SnapKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9CCDC15081A22BAED6318E3E /* SnapKit.framework */; }; + BF78C65BBEB24BF648EA2427 /* ChatMainView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6AF2493B1FE3475547B1AF00 /* ChatMainView.swift */; }; C2B0F8237715D14D8797DBC9 /* LoginView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 012F45F908769FA7C3C0792F /* LoginView.swift */; }; C94951E661D9DC9D28B56273 /* TuistAssets+CommonUI.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F625FE1E640FBDB817D76A3 /* TuistAssets+CommonUI.swift */; }; CEADBDD98AC9921C05AAC1DA /* QuizCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = E7A9CE581345578C7C0F1F1A /* QuizCollectionViewCell.swift */; }; CFB380B298BD58E955349DB3 /* DefaultNavigationBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = C69A8F84966D9255A07CB9B1 /* DefaultNavigationBar.swift */; }; D5A1FD9A23361E68C449CB95 /* RxRelay.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DDBA6D35FFEB10E15860DEAA /* RxRelay.framework */; }; + D6F86ADD009EC221F1406E4A /* FSCalendar.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 051C9BA921E7112E72ED3C05 /* FSCalendar.framework */; }; DD2A7F27C6902B1F7FB0BC1D /* Colors.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 2685DC22364927A00A14326C /* Colors.xcassets */; }; E040692C1AD240D1B76D922B /* Domain.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D7F071BE47C00467572D4D30 /* Domain.framework */; }; E055AA66777B1D4CC8C884E4 /* CommonUIAssets.swift in Sources */ = {isa = PBXBuildFile; fileRef = 75EF1CA00DA4B6E64F0CB1F0 /* CommonUIAssets.swift */; }; E0865F2849CCB89CC83D3B77 /* Coordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 27E2803575B1ED3D999440C1 /* Coordinator.swift */; }; + E3FC394B48C90764436677D2 /* ChatDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F36BED5D98EF7591C02D568E /* ChatDetailView.swift */; }; + F151F3B566BCA8E16853B241 /* ChatAnalysisLoadingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83D64639CB805B71A9CA6C17 /* ChatAnalysisLoadingView.swift */; }; F3DA12FF4D18AE405F2F6B08 /* BaseViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09C3D5AB996D321B353C1255 /* BaseViewController.swift */; }; - F7673E4248628D67F3542848 /* ChatView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3EBDD10D8389EBB23B42C54F /* ChatView.swift */; }; - FB3FE0AB8AE6868B6D5E241C /* DiaryView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C28FE6392E1612667826E5C5 /* DiaryView.swift */; }; + F8B8A383C8670A714B41F2EE /* DiaryEditView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDCDDE79ED900C96E8D0EC4A /* DiaryEditView.swift */; }; /* End PBXBuildFile section */ /* Begin PBXCopyFilesBuildPhase section */ @@ -62,45 +72,55 @@ /* Begin PBXFileReference section */ 012F45F908769FA7C3C0792F /* LoginView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoginView.swift; sourceTree = ""; }; + 051C9BA921E7112E72ED3C05 /* FSCalendar.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = FSCalendar.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 09C3D5AB996D321B353C1255 /* BaseViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BaseViewController.swift; sourceTree = ""; }; 1591A892B06D329BE11253BC /* QuizCompleteAlertView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QuizCompleteAlertView.swift; sourceTree = ""; }; + 179BF0E6EE3B59F510B8AF40 /* DiaryAddView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DiaryAddView.swift; sourceTree = ""; }; + 195B7F96DD5F38D1EB470427 /* LMAlert.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LMAlert.swift; sourceTree = ""; }; + 22C43ED73377A3ADBF7DF855 /* LMTextField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LMTextField.swift; sourceTree = ""; }; 2685DC22364927A00A14326C /* Colors.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Colors.xcassets; sourceTree = ""; }; 27E2803575B1ED3D999440C1 /* Coordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Coordinator.swift; sourceTree = ""; }; + 283B461760B2611C637CCD35 /* LMButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LMButton.swift; sourceTree = ""; }; 39A77FC9A0E2D7F62CA16E90 /* HomeQuizView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeQuizView.swift; sourceTree = ""; }; - 3EBDD10D8389EBB23B42C54F /* ChatView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatView.swift; sourceTree = ""; }; 47AA88AB7E000E21ED0BD7D8 /* HomeView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeView.swift; sourceTree = ""; }; + 4D915D93710901915F0E1B11 /* LMInputField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LMInputField.swift; sourceTree = ""; }; 5026821CC7DCD8E25C99E1BE /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; + 54D77EBBB5DC07D000BA651F /* ChatView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatView.swift; sourceTree = ""; }; + 6AF2493B1FE3475547B1AF00 /* ChatMainView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatMainView.swift; sourceTree = ""; }; 6F625FE1E640FBDB817D76A3 /* TuistAssets+CommonUI.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "TuistAssets+CommonUI.swift"; sourceTree = ""; }; 70367F4314114F624CA9A94B /* QuizView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QuizView.swift; sourceTree = ""; }; + 73E4522EABE13359D77758CA /* SignUpView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SignUpView.swift; sourceTree = ""; }; 75EF1CA00DA4B6E64F0CB1F0 /* CommonUIAssets.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CommonUIAssets.swift; sourceTree = ""; }; 7CE59FBFD1AAA1B5E7B45FD5 /* HomeProgressView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeProgressView.swift; sourceTree = ""; }; + 7DE79D12719B54383633DF73 /* SignInView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SignInView.swift; sourceTree = ""; }; + 7EEBE047F5F8EC16F0985204 /* DiaryResultView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DiaryResultView.swift; sourceTree = ""; }; + 83D64639CB805B71A9CA6C17 /* ChatAnalysisLoadingView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatAnalysisLoadingView.swift; sourceTree = ""; }; 9266801EFBA6178C5B70C641 /* MyPageView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MyPageView.swift; sourceTree = ""; }; 94EF204F3CD6F86C6167C300 /* TuistBundle+CommonUI.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "TuistBundle+CommonUI.swift"; sourceTree = ""; }; - 950A0D4E2E5AADA700C07CF2 /* SignUpView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SignUpView.swift; sourceTree = ""; }; - 950A0D502E5AADC100C07CF2 /* SignInView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SignInView.swift; sourceTree = ""; }; - 950A0D552E5C29CC00C07CF2 /* LMTextField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LMTextField.swift; sourceTree = ""; }; - 950A0D5F2E5C3C6D00C07CF2 /* LMButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LMButton.swift; sourceTree = ""; }; - 950A0D612E5C561400C07CF2 /* LMInputField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LMInputField.swift; sourceTree = ""; }; - 950A0D952E605CE300C07CF2 /* UIStackView+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIStackView+Extension.swift"; sourceTree = ""; }; - 9516ED492E7076FB00F548A1 /* ChatAnalysisView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatAnalysisView.swift; sourceTree = ""; }; - 9516ED4C2E707AC900F548A1 /* ChatAnalysisLoadingView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatAnalysisLoadingView.swift; sourceTree = ""; }; - 9516ED502E708CF200F548A1 /* ChatMainView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatMainView.swift; sourceTree = ""; }; - 9516ED522E709BA300F548A1 /* ChatListCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatListCell.swift; sourceTree = ""; }; - 956C4D6F2E767F8300E32F93 /* LMAlert.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LMAlert.swift; sourceTree = ""; }; - 956C4D732E7690E600E32F93 /* ChatDetailView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatDetailView.swift; sourceTree = ""; }; + 9532C7A82E786B3800B4BADE /* DiaryLoadingView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DiaryLoadingView.swift; sourceTree = ""; }; + 956C4D842E76F04A00E32F93 /* CalendarView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CalendarView.swift; sourceTree = ""; }; + 956C4D882E76F0F400E32F93 /* DividerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DividerView.swift; sourceTree = ""; }; + 95C68FAA2E78631D0017D28B /* DateFormatter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DateFormatter.swift; sourceTree = ""; }; + 95EDE8D42E7E60C50091ED75 /* DiaryDetailView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DiaryDetailView.swift; sourceTree = ""; }; + 963B9323B265D5683B336EF2 /* ChatAnalysisView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatAnalysisView.swift; sourceTree = ""; }; 9CCDC15081A22BAED6318E3E /* SnapKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SnapKit.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + ACC4F396C35AD4C9F54E4BF6 /* DiaryTodayView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DiaryTodayView.swift; sourceTree = ""; }; BACC7259FC0C14CB352A4E6B /* OptionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OptionView.swift; sourceTree = ""; }; - C28FE6392E1612667826E5C5 /* DiaryView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DiaryView.swift; sourceTree = ""; }; C69A8F84966D9255A07CB9B1 /* DefaultNavigationBar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DefaultNavigationBar.swift; sourceTree = ""; }; CA4151AAF9C1E241C733FF76 /* RxCocoaRuntime.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = RxCocoaRuntime.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + CD604E204E1BFC232B5C48AC /* ChatListCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatListCell.swift; sourceTree = ""; }; CDBD62030B65AE3A6A7F34D6 /* CommonUI-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = "CommonUI-Info.plist"; sourceTree = ""; }; D089568D67BA41DD38CCFD6B /* AnswerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnswerView.swift; sourceTree = ""; }; D7F071BE47C00467572D4D30 /* Domain.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Domain.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + D81F42DF8103F21916D13101 /* DiaryView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DiaryView.swift; sourceTree = ""; }; DDBA6D35FFEB10E15860DEAA /* RxRelay.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = RxRelay.framework; sourceTree = BUILT_PRODUCTS_DIR; }; E7A9CE581345578C7C0F1F1A /* QuizCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QuizCollectionViewCell.swift; sourceTree = ""; }; F0F7A08F911F04FE3F6FE36B /* CommonUI.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = CommonUI.framework; sourceTree = BUILT_PRODUCTS_DIR; }; F1321D979BCBEAF892D8C0C8 /* RxSwift.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = RxSwift.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + F36BED5D98EF7591C02D568E /* ChatDetailView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatDetailView.swift; sourceTree = ""; }; F3DE048BEDCB92B48F401061 /* RxCocoa.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = RxCocoa.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + FDCDDE79ED900C96E8D0EC4A /* DiaryEditView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DiaryEditView.swift; sourceTree = ""; }; + FF3F34507415EC124AF9DCE2 /* UIStackView+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIStackView+Extension.swift"; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -109,6 +129,7 @@ buildActionMask = 2147483647; files = ( E040692C1AD240D1B76D922B /* Domain.framework in Frameworks */, + D6F86ADD009EC221F1406E4A /* FSCalendar.framework in Frameworks */, BAD8B768F782046D4AA1C073 /* RxCocoa.framework in Frameworks */, 4A71A59F40DC619E678B1CAD /* RxCocoaRuntime.framework in Frameworks */, D5A1FD9A23361E68C449CB95 /* RxRelay.framework in Frameworks */, @@ -137,6 +158,19 @@ path = Assets; sourceTree = ""; }; + 1A870CBB14111D272540A935 /* Chat */ = { + isa = PBXGroup; + children = ( + 83D64639CB805B71A9CA6C17 /* ChatAnalysisLoadingView.swift */, + 963B9323B265D5683B336EF2 /* ChatAnalysisView.swift */, + F36BED5D98EF7591C02D568E /* ChatDetailView.swift */, + CD604E204E1BFC232B5C48AC /* ChatListCell.swift */, + 6AF2493B1FE3475547B1AF00 /* ChatMainView.swift */, + 54D77EBBB5DC07D000BA651F /* ChatView.swift */, + ); + path = Chat; + sourceTree = ""; + }; 2DBBCE824C7AA1459F0CEA0F /* Enum */ = { isa = PBXGroup; children = ( @@ -145,12 +179,20 @@ path = Enum; sourceTree = ""; }; + 45816E8E783249B256914008 /* Extension */ = { + isa = PBXGroup; + children = ( + FF3F34507415EC124AF9DCE2 /* UIStackView+Extension.swift */, + ); + path = Extension; + sourceTree = ""; + }; 4F020027E0FF96E7A77E5F05 /* Login */ = { isa = PBXGroup; children = ( 012F45F908769FA7C3C0792F /* LoginView.swift */, - 950A0D502E5AADC100C07CF2 /* SignInView.swift */, - 950A0D4E2E5AADA700C07CF2 /* SignUpView.swift */, + 7DE79D12719B54383633DF73 /* SignInView.swift */, + 73E4522EABE13359D77758CA /* SignUpView.swift */, ); path = Login; sourceTree = ""; @@ -188,51 +230,42 @@ path = Base; sourceTree = ""; }; - 950A0D522E5C296400C07CF2 /* Component */ = { - isa = PBXGroup; - children = ( - 956C4D6F2E767F8300E32F93 /* LMAlert.swift */, - 950A0D552E5C29CC00C07CF2 /* LMTextField.swift */, - 950A0D5F2E5C3C6D00C07CF2 /* LMButton.swift */, - 950A0D612E5C561400C07CF2 /* LMInputField.swift */, - ); - path = Component; - sourceTree = ""; - }; - 950A0D942E605CE100C07CF2 /* Extension */ = { + 956C4D942E78623D00E32F93 /* Formatter */ = { isa = PBXGroup; children = ( - 950A0D952E605CE300C07CF2 /* UIStackView+Extension.swift */, + 95C68FAA2E78631D0017D28B /* DateFormatter.swift */, ); - path = Extension; - sourceTree = ""; - }; - 951F3F852E6DDE7F0022583B /* Chat */ = { - isa = PBXGroup; - children = ( - 9516ED502E708CF200F548A1 /* ChatMainView.swift */, - 9516ED522E709BA300F548A1 /* ChatListCell.swift */, - 3EBDD10D8389EBB23B42C54F /* ChatView.swift */, - 956C4D732E7690E600E32F93 /* ChatDetailView.swift */, - 9516ED4C2E707AC900F548A1 /* ChatAnalysisLoadingView.swift */, - 9516ED492E7076FB00F548A1 /* ChatAnalysisView.swift */, - ); - path = Chat; + path = Formatter; sourceTree = ""; }; 9786E1056828B1E6D5EDAB7C /* View */ = { isa = PBXGroup; children = ( + 1A870CBB14111D272540A935 /* Chat */, + A4CE5E0046457BDF29D0FDBE /* Diary */, DFA6DB2905DB98838A8AEA6F /* Home */, 4F020027E0FF96E7A77E5F05 /* Login */, 7F5AEF3C3719E8689E7AF017 /* Navigation */, - 951F3F852E6DDE7F0022583B /* Chat */, - C28FE6392E1612667826E5C5 /* DiaryView.swift */, 9266801EFBA6178C5B70C641 /* MyPageView.swift */, ); path = View; sourceTree = ""; }; + A4CE5E0046457BDF29D0FDBE /* Diary */ = { + isa = PBXGroup; + children = ( + D81F42DF8103F21916D13101 /* DiaryView.swift */, + 956C4D842E76F04A00E32F93 /* CalendarView.swift */, + 179BF0E6EE3B59F510B8AF40 /* DiaryAddView.swift */, + FDCDDE79ED900C96E8D0EC4A /* DiaryEditView.swift */, + 9532C7A82E786B3800B4BADE /* DiaryLoadingView.swift */, + 95EDE8D42E7E60C50091ED75 /* DiaryDetailView.swift */, + 7EEBE047F5F8EC16F0985204 /* DiaryResultView.swift */, + ACC4F396C35AD4C9F54E4BF6 /* DiaryTodayView.swift */, + ); + path = Diary; + sourceTree = ""; + }; B7342C91FB26C6BB1D507B58 = { isa = PBXGroup; children = ( @@ -244,17 +277,30 @@ BADD047B94176A526A5B7FB2 /* Sources */ = { isa = PBXGroup; children = ( - 950A0D942E605CE100C07CF2 /* Extension */, - 950A0D522E5C296400C07CF2 /* Component */, + 956C4D942E78623D00E32F93 /* Formatter */, 15CD642D82E9115C7976C408 /* Assets */, 88FA467F361B11350139B775 /* Base */, + BCBFE28A82364D98B26F502F /* Component */, 532B9EC7299244FE28190921 /* Coordinator */, 2DBBCE824C7AA1459F0CEA0F /* Enum */, + 45816E8E783249B256914008 /* Extension */, 9786E1056828B1E6D5EDAB7C /* View */, ); path = Sources; sourceTree = ""; }; + BCBFE28A82364D98B26F502F /* Component */ = { + isa = PBXGroup; + children = ( + 956C4D882E76F0F400E32F93 /* DividerView.swift */, + 195B7F96DD5F38D1EB470427 /* LMAlert.swift */, + 283B461760B2611C637CCD35 /* LMButton.swift */, + 4D915D93710901915F0E1B11 /* LMInputField.swift */, + 22C43ED73377A3ADBF7DF855 /* LMTextField.swift */, + ); + path = Component; + sourceTree = ""; + }; DFA6DB2905DB98838A8AEA6F /* Home */ = { isa = PBXGroup; children = ( @@ -275,6 +321,7 @@ children = ( F0F7A08F911F04FE3F6FE36B /* CommonUI.framework */, D7F071BE47C00467572D4D30 /* Domain.framework */, + 051C9BA921E7112E72ED3C05 /* FSCalendar.framework */, F3DE048BEDCB92B48F401061 /* RxCocoa.framework */, CA4151AAF9C1E241C733FF76 /* RxCocoaRuntime.framework */, DDBA6D35FFEB10E15860DEAA /* RxRelay.framework */, @@ -368,34 +415,43 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 9516ED4A2E7076FF00F548A1 /* ChatAnalysisView.swift in Sources */, - 950A0D602E5C3C7000C07CF2 /* LMButton.swift in Sources */, C94951E661D9DC9D28B56273 /* TuistAssets+CommonUI.swift in Sources */, 7DC59B80630854028C7C80F4 /* TuistBundle+CommonUI.swift in Sources */, F3DA12FF4D18AE405F2F6B08 /* BaseViewController.swift in Sources */, + A08AEF6AF6E193802D530204 /* LMAlert.swift in Sources */, + 96EC2CC574B6ED29C83445A2 /* LMButton.swift in Sources */, + A8D633A6C67CC42F3E7017EA /* LMInputField.swift in Sources */, + 21B9DB651F4D3F7DFB5CE055 /* LMTextField.swift in Sources */, E0865F2849CCB89CC83D3B77 /* Coordinator.swift in Sources */, - 950A0D962E605CEA00C07CF2 /* UIStackView+Extension.swift in Sources */, E055AA66777B1D4CC8C884E4 /* CommonUIAssets.swift in Sources */, - 950A0D4F2E5AADB500C07CF2 /* SignUpView.swift in Sources */, - 956C4D742E7690EA00E32F93 /* ChatDetailView.swift in Sources */, - F7673E4248628D67F3542848 /* ChatView.swift in Sources */, - 950A0D562E5C29D000C07CF2 /* LMTextField.swift in Sources */, - 9516ED4D2E707ACF00F548A1 /* ChatAnalysisLoadingView.swift in Sources */, - FB3FE0AB8AE6868B6D5E241C /* DiaryView.swift in Sources */, + 956C4D852E76F05000E32F93 /* CalendarView.swift in Sources */, + B9F3C0F9AA675E70FB8ED969 /* UIStackView+Extension.swift in Sources */, + F151F3B566BCA8E16853B241 /* ChatAnalysisLoadingView.swift in Sources */, + 8E0E6BDEA9574116ECBD55E4 /* ChatAnalysisView.swift in Sources */, + E3FC394B48C90764436677D2 /* ChatDetailView.swift in Sources */, + 3DE37C9DF3C45F1BE84AB98E /* ChatListCell.swift in Sources */, + BF78C65BBEB24BF648EA2427 /* ChatMainView.swift in Sources */, + 451E4F690605A30900E088FE /* ChatView.swift in Sources */, + 736D1C5EB53FB204B030C397 /* DiaryAddView.swift in Sources */, + 9532C7A92E786B3C00B4BADE /* DiaryLoadingView.swift in Sources */, + F8B8A383C8670A714B41F2EE /* DiaryEditView.swift in Sources */, + 7DD1D21C569B040F1AB7067B /* DiaryResultView.swift in Sources */, + 95EDE8D52E7E60CA0091ED75 /* DiaryDetailView.swift in Sources */, + 568C33E681D9B58B71273564 /* DiaryTodayView.swift in Sources */, + 318A2464888400F05D38B7E5 /* DiaryView.swift in Sources */, + 956C4D892E76F0F500E32F93 /* DividerView.swift in Sources */, 70139D721530B3262C44ABC1 /* AnswerView.swift in Sources */, 416C58AEE5E4491991982CFF /* HomeProgressView.swift in Sources */, 7D319882A302F75CCE46A48C /* HomeQuizView.swift in Sources */, 537F80B2F39FD73F6F78F9B2 /* HomeView.swift in Sources */, - 9516ED532E709BA600F548A1 /* ChatListCell.swift in Sources */, - 950A0D622E5C562700C07CF2 /* LMInputField.swift in Sources */, - 9516ED512E708CF700F548A1 /* ChatMainView.swift in Sources */, 65762CE867888754D56BA2CB /* OptionView.swift in Sources */, + 95C68FAB2E7863210017D28B /* DateFormatter.swift in Sources */, CEADBDD98AC9921C05AAC1DA /* QuizCollectionViewCell.swift in Sources */, - 950A0D512E5AADC400C07CF2 /* SignInView.swift in Sources */, 592FAEA836FD6B4B3F466D72 /* QuizCompleteAlertView.swift in Sources */, - 956C4D702E767F8800E32F93 /* LMAlert.swift in Sources */, 65B3402D15A9F06B3E88CE19 /* QuizView.swift in Sources */, C2B0F8237715D14D8797DBC9 /* LoginView.swift in Sources */, + 43D87B408B5AEBF565104F7E /* SignInView.swift in Sources */, + 0E81854D9C2CC55C24DEA5A8 /* SignUpView.swift in Sources */, 473C96577A92DD6F71241A76 /* MyPageView.swift in Sources */, CFB380B298BD58E955349DB3 /* DefaultNavigationBar.swift in Sources */, ); @@ -415,6 +471,7 @@ DYLIB_INSTALL_NAME_BASE = "@rpath"; HEADER_SEARCH_PATHS = ( "$(inherited)", + "$(SRCROOT)/../../Tuist/.build/checkouts/FSCalendar/FSCalendar/include", "$(SRCROOT)/../../Tuist/.build/checkouts/RxSwift/Sources/RxCocoaRuntime/include", ); INFOPLIST_FILE = "Derived/InfoPlists/CommonUI-Info.plist"; @@ -426,9 +483,10 @@ ); OTHER_CFLAGS = ( "$(inherited)", + "-fmodule-map-file=$(SRCROOT)/../../Tuist/.build/checkouts/FSCalendar/FSCalendar/include/module.modulemap", "-fmodule-map-file=$(SRCROOT)/../../Tuist/.build/tuist-derived/RxCocoaRuntime/RxCocoaRuntime.modulemap", ); - OTHER_SWIFT_FLAGS = "$(inherited) -Xcc -fmodule-map-file=$(SRCROOT)/../../Tuist/.build/tuist-derived/RxCocoaRuntime/RxCocoaRuntime.modulemap"; + OTHER_SWIFT_FLAGS = "$(inherited) -Xcc -fmodule-map-file=$(SRCROOT)/../../Tuist/.build/checkouts/FSCalendar/FSCalendar/include/module.modulemap -Xcc -fmodule-map-file=$(SRCROOT)/../../Tuist/.build/tuist-derived/RxCocoaRuntime/RxCocoaRuntime.modulemap"; PRODUCT_BUNDLE_IDENTIFIER = io.tuist.CommonUI; PRODUCT_NAME = CommonUI; SDKROOT = iphoneos; @@ -437,7 +495,7 @@ SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; SWIFT_COMPILATION_MODE = wholemodule; - SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; + SWIFT_OPTIMIZATION_LEVEL = "-O"; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; VERSIONING_SYSTEM = "apple-generic"; @@ -509,6 +567,7 @@ DYLIB_INSTALL_NAME_BASE = "@rpath"; HEADER_SEARCH_PATHS = ( "$(inherited)", + "$(SRCROOT)/../../Tuist/.build/checkouts/FSCalendar/FSCalendar/include", "$(SRCROOT)/../../Tuist/.build/checkouts/RxSwift/Sources/RxCocoaRuntime/include", ); INFOPLIST_FILE = "Derived/InfoPlists/CommonUI-Info.plist"; @@ -520,9 +579,10 @@ ); OTHER_CFLAGS = ( "$(inherited)", + "-fmodule-map-file=$(SRCROOT)/../../Tuist/.build/checkouts/FSCalendar/FSCalendar/include/module.modulemap", "-fmodule-map-file=$(SRCROOT)/../../Tuist/.build/tuist-derived/RxCocoaRuntime/RxCocoaRuntime.modulemap", ); - OTHER_SWIFT_FLAGS = "$(inherited) -Xcc -fmodule-map-file=$(SRCROOT)/../../Tuist/.build/tuist-derived/RxCocoaRuntime/RxCocoaRuntime.modulemap"; + OTHER_SWIFT_FLAGS = "$(inherited) -Xcc -fmodule-map-file=$(SRCROOT)/../../Tuist/.build/checkouts/FSCalendar/FSCalendar/include/module.modulemap -Xcc -fmodule-map-file=$(SRCROOT)/../../Tuist/.build/tuist-derived/RxCocoaRuntime/RxCocoaRuntime.modulemap"; PRODUCT_BUNDLE_IDENTIFIER = io.tuist.CommonUI; PRODUCT_NAME = CommonUI; SDKROOT = iphoneos; diff --git a/Projects/CommonUI/Derived/Sources/TuistAssets+CommonUI.swift b/Projects/CommonUI/Derived/Sources/TuistAssets+CommonUI.swift index bdb751a..d5060b5 100644 --- a/Projects/CommonUI/Derived/Sources/TuistAssets+CommonUI.swift +++ b/Projects/CommonUI/Derived/Sources/TuistAssets+CommonUI.swift @@ -1,156 +1,174 @@ -//// swiftlint:disable:this file_name -//// swiftlint:disable all -//// swift-format-ignore-file -//// swiftformat:disable all -//// Generated using tuist — https://github.com/tuist/tuist -// -//#if os(macOS) -// import AppKit -//#elseif os(iOS) -// import UIKit -//#elseif os(tvOS) || os(watchOS) -// import UIKit -//#endif -//#if canImport(SwiftUI) -// import SwiftUI -//#endif -// -//// MARK: - Asset Catalogs -// -//public enum CommonUIAsset: Sendable { -// public enum Colors { -// public static let lmBlack = CommonUIColors(name: "LMBlack") -// public static let lmGray01 = CommonUIColors(name: "LMGray01") -// public static let lmGray03 = CommonUIColors(name: "LMGray03") -// public static let lmGray05 = CommonUIColors(name: "LMGray05") -// public static let lmWhite = CommonUIColors(name: "LMWhite") -// public static let lmOrange01 = CommonUIColors(name: "LMOrange01") -// public static let lmOrange03 = CommonUIColors(name: "LMOrange03") -// public static let lmOrange04 = CommonUIColors(name: "LMOrange04") -// } -// public enum Images { -// public static let back = CommonUIImages(name: "back") -// public static let play = CommonUIImages(name: "play") -// public static let smallLogo = CommonUIImages(name: "small_logo") -// public static let tabIconChat = CommonUIImages(name: "tab_icon_chat") -// public static let tabIconChatSelected = CommonUIImages(name: "tab_icon_chat_selected") -// public static let tabIconDiary = CommonUIImages(name: "tab_icon_diary") -// public static let tabIconDiarySelected = CommonUIImages(name: "tab_icon_diary_selected") -// public static let tabIconHome = CommonUIImages(name: "tab_icon_home") -// public static let tabIconHomeSelected = CommonUIImages(name: "tab_icon_home_selected") -// public static let tabIconMypage = CommonUIImages(name: "tab_icon_mypage") -// public static let tabIconMypageSelected = CommonUIImages(name: "tab_icon_mypage_selected") -// public static let tabIconStats = CommonUIImages(name: "tab_icon_stats") -// public static let tabIconStatsSelected = CommonUIImages(name: "tab_icon_stats_selected") -// } -//} -// -//// MARK: - Implementation Details -// -//public final class CommonUIColors: Sendable { -// public let name: String -// -// #if os(macOS) -// public typealias Color = NSColor -// #elseif os(iOS) || os(tvOS) || os(watchOS) || os(visionOS) -// public typealias Color = UIColor -// #endif -// -// @available(iOS 11.0, tvOS 11.0, watchOS 4.0, macOS 10.13, visionOS 1.0, *) -// public var color: Color { -// guard let color = Color(asset: self) else { -// fatalError("Unable to load color asset named \(name).") -// } -// return color -// } -// -// #if canImport(SwiftUI) -// @available(iOS 13.0, tvOS 13.0, watchOS 6.0, macOS 10.15, visionOS 1.0, *) -// public var swiftUIColor: SwiftUI.Color { -// return SwiftUI.Color(asset: self) -// } -// #endif -// -// fileprivate init(name: String) { -// self.name = name -// } -//} -// -//public extension CommonUIColors.Color { -// @available(iOS 11.0, tvOS 11.0, watchOS 4.0, macOS 10.13, visionOS 1.0, *) -// convenience init?(asset: CommonUIColors) { -// let bundle = Bundle.module -// #if os(iOS) || os(tvOS) || os(visionOS) -// self.init(named: asset.name, in: bundle, compatibleWith: nil) -// #elseif os(macOS) -// self.init(named: NSColor.Name(asset.name), bundle: bundle) -// #elseif os(watchOS) -// self.init(named: asset.name) -// #endif -// } -//} -// -//#if canImport(SwiftUI) -//@available(iOS 13.0, tvOS 13.0, watchOS 6.0, macOS 10.15, visionOS 1.0, *) -//public extension SwiftUI.Color { -// init(asset: CommonUIColors) { -// let bundle = Bundle.module -// self.init(asset.name, bundle: bundle) -// } -//} -//#endif -// -//public struct CommonUIImages: Sendable { -// public let name: String -// -// #if os(macOS) -// public typealias Image = NSImage -// #elseif os(iOS) || os(tvOS) || os(watchOS) || os(visionOS) -// public typealias Image = UIImage -// #endif -// -// public var image: Image { -// let bundle = Bundle.module -// #if os(iOS) || os(tvOS) || os(visionOS) -// let image = Image(named: name, in: bundle, compatibleWith: nil) -// #elseif os(macOS) -// let image = bundle.image(forResource: NSImage.Name(name)) -// #elseif os(watchOS) -// let image = Image(named: name) -// #endif -// guard let result = image else { -// fatalError("Unable to load image asset named \(name).") -// } -// return result -// } -// -// #if canImport(SwiftUI) -// @available(iOS 13.0, tvOS 13.0, watchOS 6.0, macOS 10.15, visionOS 1.0, *) -// public var swiftUIImage: SwiftUI.Image { -// SwiftUI.Image(asset: self) -// } -// #endif -//} -// -//#if canImport(SwiftUI) -//@available(iOS 13.0, tvOS 13.0, watchOS 6.0, macOS 10.15, visionOS 1.0, *) -//public extension SwiftUI.Image { -// init(asset: CommonUIImages) { -// let bundle = Bundle.module -// self.init(asset.name, bundle: bundle) -// } -// -// init(asset: CommonUIImages, label: Text) { -// let bundle = Bundle.module -// self.init(asset.name, bundle: bundle, label: label) -// } -// -// init(decorative asset: CommonUIImages) { -// let bundle = Bundle.module -// self.init(decorative: asset.name, bundle: bundle) -// } -//} -//#endif -// -//// swiftformat:enable all -//// swiftlint:enable all +// swiftlint:disable:this file_name +// swiftlint:disable all +// swift-format-ignore-file +// swiftformat:disable all +// Generated using tuist — https://github.com/tuist/tuist + +#if os(macOS) + import AppKit +#elseif os(iOS) + import UIKit +#elseif os(tvOS) || os(watchOS) + import UIKit +#endif +#if canImport(SwiftUI) + import SwiftUI +#endif + +// MARK: - Asset Catalogs + +public enum CommonUIAsset: Sendable { + public enum Colors { + public static let lmBlack = CommonUIColors(name: "LMBlack") + public static let lmGray01 = CommonUIColors(name: "LMGray01") + public static let lmGray03 = CommonUIColors(name: "LMGray03") + public static let lmGray04 = CommonUIColors(name: "LMGray04") + public static let lmGray05 = CommonUIColors(name: "LMGray05") + public static let lmGray06 = CommonUIColors(name: "LMGray06") + public static let lmWhite = CommonUIColors(name: "LMWhite") + public static let lmBlue = CommonUIColors(name: "LMBlue") + public static let lmBlue02 = CommonUIColors(name: "LMBlue02") + public static let lmGreen = CommonUIColors(name: "LMGreen") + public static let lmOrange01 = CommonUIColors(name: "LMOrange01") + public static let lmOrange03 = CommonUIColors(name: "LMOrange03") + public static let lmOrange04 = CommonUIColors(name: "LMOrange04") + public static let lmRed = CommonUIColors(name: "LMRed") + public static let lmRed02 = CommonUIColors(name: "LMRed02") + } + public enum Images { + public static let add = CommonUIImages(name: "add") + public static let back = CommonUIImages(name: "back") + public static let bubble = CommonUIImages(name: "bubble") + public static let close = CommonUIImages(name: "close") + public static let edit = CommonUIImages(name: "edit") + public static let message = CommonUIImages(name: "message") + public static let next = CommonUIImages(name: "next") + public static let play = CommonUIImages(name: "play") + public static let previous = CommonUIImages(name: "previous") + public static let send = CommonUIImages(name: "send") + public static let apple = CommonUIImages(name: "apple") + public static let google = CommonUIImages(name: "google") + public static let logo = CommonUIImages(name: "logo") + public static let smallLogo = CommonUIImages(name: "small_logo") + public static let tabIconChat = CommonUIImages(name: "tab_icon_chat") + public static let tabIconChatSelected = CommonUIImages(name: "tab_icon_chat_selected") + public static let tabIconDiary = CommonUIImages(name: "tab_icon_diary") + public static let tabIconDiarySelected = CommonUIImages(name: "tab_icon_diary_selected") + public static let tabIconHome = CommonUIImages(name: "tab_icon_home") + public static let tabIconHomeSelected = CommonUIImages(name: "tab_icon_home_selected") + public static let tabIconMypage = CommonUIImages(name: "tab_icon_mypage") + public static let tabIconMypageSelected = CommonUIImages(name: "tab_icon_mypage_selected") + public static let tabIconStats = CommonUIImages(name: "tab_icon_stats") + public static let tabIconStatsSelected = CommonUIImages(name: "tab_icon_stats_selected") + } +} + +// MARK: - Implementation Details + +public final class CommonUIColors: Sendable { + public let name: String + + #if os(macOS) + public typealias Color = NSColor + #elseif os(iOS) || os(tvOS) || os(watchOS) || os(visionOS) + public typealias Color = UIColor + #endif + + @available(iOS 11.0, tvOS 11.0, watchOS 4.0, macOS 10.13, visionOS 1.0, *) + public var color: Color { + guard let color = Color(asset: self) else { + fatalError("Unable to load color asset named \(name).") + } + return color + } + + #if canImport(SwiftUI) + @available(iOS 13.0, tvOS 13.0, watchOS 6.0, macOS 10.15, visionOS 1.0, *) + public var swiftUIColor: SwiftUI.Color { + return SwiftUI.Color(asset: self) + } + #endif + + fileprivate init(name: String) { + self.name = name + } +} + +public extension CommonUIColors.Color { + @available(iOS 11.0, tvOS 11.0, watchOS 4.0, macOS 10.13, visionOS 1.0, *) + convenience init?(asset: CommonUIColors) { + let bundle = Bundle.module + #if os(iOS) || os(tvOS) || os(visionOS) + self.init(named: asset.name, in: bundle, compatibleWith: nil) + #elseif os(macOS) + self.init(named: NSColor.Name(asset.name), bundle: bundle) + #elseif os(watchOS) + self.init(named: asset.name) + #endif + } +} + +#if canImport(SwiftUI) +@available(iOS 13.0, tvOS 13.0, watchOS 6.0, macOS 10.15, visionOS 1.0, *) +public extension SwiftUI.Color { + init(asset: CommonUIColors) { + let bundle = Bundle.module + self.init(asset.name, bundle: bundle) + } +} +#endif + +public struct CommonUIImages: Sendable { + public let name: String + + #if os(macOS) + public typealias Image = NSImage + #elseif os(iOS) || os(tvOS) || os(watchOS) || os(visionOS) + public typealias Image = UIImage + #endif + + public var image: Image { + let bundle = Bundle.module + #if os(iOS) || os(tvOS) || os(visionOS) + let image = Image(named: name, in: bundle, compatibleWith: nil) + #elseif os(macOS) + let image = bundle.image(forResource: NSImage.Name(name)) + #elseif os(watchOS) + let image = Image(named: name) + #endif + guard let result = image else { + fatalError("Unable to load image asset named \(name).") + } + return result + } + + #if canImport(SwiftUI) + @available(iOS 13.0, tvOS 13.0, watchOS 6.0, macOS 10.15, visionOS 1.0, *) + public var swiftUIImage: SwiftUI.Image { + SwiftUI.Image(asset: self) + } + #endif +} + +#if canImport(SwiftUI) +@available(iOS 13.0, tvOS 13.0, watchOS 6.0, macOS 10.15, visionOS 1.0, *) +public extension SwiftUI.Image { + init(asset: CommonUIImages) { + let bundle = Bundle.module + self.init(asset.name, bundle: bundle) + } + + init(asset: CommonUIImages, label: Text) { + let bundle = Bundle.module + self.init(asset.name, bundle: bundle, label: label) + } + + init(decorative asset: CommonUIImages) { + let bundle = Bundle.module + self.init(decorative: asset.name, bundle: bundle) + } +} +#endif + +// swiftformat:enable all +// swiftlint:enable all diff --git a/Projects/CommonUI/Derived/Sources/TuistBundle+CommonUI.swift b/Projects/CommonUI/Derived/Sources/TuistBundle+CommonUI.swift index 3aac6c9..cf585b1 100644 --- a/Projects/CommonUI/Derived/Sources/TuistBundle+CommonUI.swift +++ b/Projects/CommonUI/Derived/Sources/TuistBundle+CommonUI.swift @@ -1,25 +1,25 @@ -//// periphery:ignore:all -//// swiftlint:disable:this file_name -//// swiftlint:disable all -//// swift-format-ignore-file -//// swiftformat:disable all -//#if hasFeature(InternalImportsByDefault) -//public import Foundation -//#else -//import Foundation -//#endif -//// MARK: - Swift Bundle Accessor for Frameworks -//private class BundleFinder {} -//extension Foundation.Bundle { -///// Since CommonUI is a dynamic framework, the bundle for classes within this module can be used directly. -// static let module = Bundle(for: BundleFinder.self) -//} -//// MARK: - Objective-C Bundle Accessor -//@objc -//public class CommonUIResources: NSObject { -//@objc public class var bundle: Bundle { -// return .module -//} -//} -//// swiftformat:enable all -//// swiftlint:enable all +// periphery:ignore:all +// swiftlint:disable:this file_name +// swiftlint:disable all +// swift-format-ignore-file +// swiftformat:disable all +#if hasFeature(InternalImportsByDefault) +public import Foundation +#else +import Foundation +#endif +// MARK: - Swift Bundle Accessor for Frameworks +private class BundleFinder {} +extension Foundation.Bundle { +/// Since CommonUI is a dynamic framework, the bundle for classes within this module can be used directly. + static let module = Bundle(for: BundleFinder.self) +} +// MARK: - Objective-C Bundle Accessor +@objc +public class CommonUIResources: NSObject { +@objc public class var bundle: Bundle { + return .module +} +} +// swiftformat:enable all +// swiftlint:enable all \ No newline at end of file diff --git a/Projects/CommonUI/Project.swift b/Projects/CommonUI/Project.swift index 979e619..a74052b 100644 --- a/Projects/CommonUI/Project.swift +++ b/Projects/CommonUI/Project.swift @@ -14,7 +14,8 @@ let project = Project( .project(target: "Domain", path: "../Domain"), .external(name: "SnapKit"), .external(name: "RxSwift"), - .external(name: "RxCocoa") + .external(name: "RxCocoa"), + .external(name: "FSCalendar") ] ) ] diff --git a/Projects/CommonUI/Sources/Assets/Images.xcassets/Icon/add.imageset/Contents.json b/Projects/CommonUI/Sources/Assets/Images.xcassets/Icon/add.imageset/Contents.json new file mode 100644 index 0000000..b7fcfb9 --- /dev/null +++ b/Projects/CommonUI/Sources/Assets/Images.xcassets/Icon/add.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "add@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "add@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Projects/CommonUI/Sources/Assets/Images.xcassets/Icon/add.imageset/add@2x.png b/Projects/CommonUI/Sources/Assets/Images.xcassets/Icon/add.imageset/add@2x.png new file mode 100644 index 0000000..7e5716f Binary files /dev/null and b/Projects/CommonUI/Sources/Assets/Images.xcassets/Icon/add.imageset/add@2x.png differ diff --git a/Projects/CommonUI/Sources/Assets/Images.xcassets/Icon/add.imageset/add@3x.png b/Projects/CommonUI/Sources/Assets/Images.xcassets/Icon/add.imageset/add@3x.png new file mode 100644 index 0000000..00cd212 Binary files /dev/null and b/Projects/CommonUI/Sources/Assets/Images.xcassets/Icon/add.imageset/add@3x.png differ diff --git a/Projects/CommonUI/Sources/Assets/Images.xcassets/Icon/next.imageset/Contents.json b/Projects/CommonUI/Sources/Assets/Images.xcassets/Icon/next.imageset/Contents.json new file mode 100644 index 0000000..5e1d0ec --- /dev/null +++ b/Projects/CommonUI/Sources/Assets/Images.xcassets/Icon/next.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "next@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "next@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Projects/CommonUI/Sources/Assets/Images.xcassets/Icon/next.imageset/next@2x.png b/Projects/CommonUI/Sources/Assets/Images.xcassets/Icon/next.imageset/next@2x.png new file mode 100644 index 0000000..3d1664b Binary files /dev/null and b/Projects/CommonUI/Sources/Assets/Images.xcassets/Icon/next.imageset/next@2x.png differ diff --git a/Projects/CommonUI/Sources/Assets/Images.xcassets/Icon/next.imageset/next@3x.png b/Projects/CommonUI/Sources/Assets/Images.xcassets/Icon/next.imageset/next@3x.png new file mode 100644 index 0000000..8916420 Binary files /dev/null and b/Projects/CommonUI/Sources/Assets/Images.xcassets/Icon/next.imageset/next@3x.png differ diff --git a/Projects/CommonUI/Sources/Assets/Images.xcassets/Icon/previous.imageset/Contents.json b/Projects/CommonUI/Sources/Assets/Images.xcassets/Icon/previous.imageset/Contents.json new file mode 100644 index 0000000..8419332 --- /dev/null +++ b/Projects/CommonUI/Sources/Assets/Images.xcassets/Icon/previous.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "previous@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "previous@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Projects/CommonUI/Sources/Assets/Images.xcassets/Icon/previous.imageset/previous@2x.png b/Projects/CommonUI/Sources/Assets/Images.xcassets/Icon/previous.imageset/previous@2x.png new file mode 100644 index 0000000..005503d Binary files /dev/null and b/Projects/CommonUI/Sources/Assets/Images.xcassets/Icon/previous.imageset/previous@2x.png differ diff --git a/Projects/CommonUI/Sources/Assets/Images.xcassets/Icon/previous.imageset/previous@3x.png b/Projects/CommonUI/Sources/Assets/Images.xcassets/Icon/previous.imageset/previous@3x.png new file mode 100644 index 0000000..6e2aaee Binary files /dev/null and b/Projects/CommonUI/Sources/Assets/Images.xcassets/Icon/previous.imageset/previous@3x.png differ diff --git a/Projects/CommonUI/Sources/Component/DividerView.swift b/Projects/CommonUI/Sources/Component/DividerView.swift new file mode 100644 index 0000000..5ffc53e --- /dev/null +++ b/Projects/CommonUI/Sources/Component/DividerView.swift @@ -0,0 +1,63 @@ +// +// DividerView.swift +// CommonUI +// +// Created by 박지윤 on 9/14/25. +// + +import UIKit + +enum Divider { + case thin + case thick + + var height: CGFloat { + switch self { + case .thin: + return 1.0 + case .thick: + return 8.0 + } + } + + var color: UIColor { + return CommonUIAssets.LMGray5 ?? .lightGray + } +} + +class DividerView: UIView { + + // MARK: UI Component + private let dividerView = UIView() + + // MARK: Properties + private var dividerType: Divider + + // MARK: Initializer + init(dividerType: Divider) { + self.dividerType = dividerType + super.init(frame: .zero) + + configureSubviews() + makeConstraints() + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + // MARK: Configuration + func configureSubviews() { + dividerView.backgroundColor = dividerType.color + + addSubview(dividerView) + } + + // MARK: Layout + func makeConstraints() { + dividerView.snp.makeConstraints { + $0.horizontalEdges.equalToSuperview() + $0.height.equalTo(dividerType.height) + } + } +} diff --git a/Projects/CommonUI/Sources/Component/LMButton.swift b/Projects/CommonUI/Sources/Component/LMButton.swift index da233ee..6f595de 100644 --- a/Projects/CommonUI/Sources/Component/LMButton.swift +++ b/Projects/CommonUI/Sources/Component/LMButton.swift @@ -39,4 +39,10 @@ public class LMButton: UIButton { $0.height.equalTo(55) } } + + public func setHeight(_ height: CGFloat) { + self.snp.updateConstraints { + $0.height.equalTo(height) + } + } } diff --git a/Projects/CommonUI/Sources/Enum/CommonUIAssets.swift b/Projects/CommonUI/Sources/Enum/CommonUIAssets.swift index 380317e..f4ff5af 100644 --- a/Projects/CommonUI/Sources/Enum/CommonUIAssets.swift +++ b/Projects/CommonUI/Sources/Enum/CommonUIAssets.swift @@ -54,6 +54,9 @@ public enum CommonUIAssets { public static let IconEdit = image(named: "edit") public static let IconMessage = image(named: "message") public static let IconBubble = image(named: "bubble") + public static let IconPrevious = image(named: "previous") + public static let IconNext = image(named: "next") + public static let IconAdd = image(named: "add") /// color public static let LMOrange1 = color(named: "LMOrange01") diff --git a/Projects/CommonUI/Sources/Formatter/DateFormatter.swift b/Projects/CommonUI/Sources/Formatter/DateFormatter.swift new file mode 100644 index 0000000..8769717 --- /dev/null +++ b/Projects/CommonUI/Sources/Formatter/DateFormatter.swift @@ -0,0 +1,84 @@ +// +// DateFormatter.swift +// CommonUI +// +// Created by 박지윤 on 9/16/25. +// + +import Foundation + +extension Date { + func toString(format: String) -> String { + let dateFormatter = DateFormatter() + dateFormatter.dateFormat = format + return dateFormatter.string(from: self) + } + + func toDateString() -> String { + let dateFormatter = DateFormatter() + dateFormatter.dateFormat = "yyyy년 MM월 dd일" + dateFormatter.locale = Locale(identifier: "ko_KR") + return dateFormatter.string(from: self) + } + + func getToday() -> String { + let today = Date() + + return today.toDateString() + } + + func getAddingDay(_ daysOffset: Int) -> String { + let day = Calendar.current.date(byAdding: .day, value: daysOffset, to: self) ?? self + + return day.toDateString() + } +} + +extension String { + func convertDateString(fromFormat: String, toFormat: String) -> String? { + let dateFormatter = DateFormatter() + + dateFormatter.dateFormat = fromFormat + guard let date = dateFormatter.date(from: self) else { + return nil + } + + dateFormatter.dateFormat = toFormat + return dateFormatter.string(from: date) + } + + func toDate(format: String) -> Date? { + let dateFormatter = DateFormatter() + dateFormatter.dateFormat = format + return dateFormatter.date(from: self) + } + + func previousMonth(format: String) -> String? { + guard let currentDate = self.toDate(format: format) else { return nil } + + let calendar = Calendar.current + if let previousMonthDate = calendar.date(byAdding: .month, value: -1, to: currentDate) { + return previousMonthDate.toString(format: format) + } + return nil + } + + func nextMonth(format: String) -> String? { + guard let currentDate = self.toDate(format: format) else { return nil } + + let calendar = Calendar.current + if let nextMonthDate = calendar.date(byAdding: .month, value: 1, to: currentDate) { + return nextMonthDate.toString(format: format) + } + return nil + } +} + +extension DateFormatter { + static func localizedStringToDate(_ dateString: String, format: String = "yyyy-MM-dd") -> Date? { + let formatter = DateFormatter() + formatter.dateFormat = format + formatter.locale = Locale(identifier: "ko_KR") + return formatter.date(from: dateString) + } +} diff --git a/Projects/CommonUI/Sources/View/Diary/CalendarView.swift b/Projects/CommonUI/Sources/View/Diary/CalendarView.swift new file mode 100644 index 0000000..0c990ee --- /dev/null +++ b/Projects/CommonUI/Sources/View/Diary/CalendarView.swift @@ -0,0 +1,317 @@ +// +// CalendarView.swift +// CommonUI +// +// Created by 박지윤 on 9/14/25. +// + +import UIKit +import SnapKit + +public final class CalendarView: UIView { + // MARK: UI Components + private let weekdayStackView = UIStackView().then { + $0.axis = .horizontal + $0.distribution = .fillEqually + $0.backgroundColor = CommonUIAssets.LMOrange4 + } + + private let calendarCollectionView: UICollectionView = { + let layout = UICollectionViewFlowLayout() + layout.minimumInteritemSpacing = 0 + layout.minimumLineSpacing = 0 + let collectionView = UICollectionView(frame: .zero, collectionViewLayout: layout) + collectionView.backgroundColor = CommonUIAssets.LMOrange4 + collectionView.register(CalendarCell.self, forCellWithReuseIdentifier: CalendarCell.identifier) + return collectionView + }() + + // MARK: Properties + public var tapDay: ((Date) -> Void)? // 날짜 문자열 (yyyy-MM-dd 형식) + private var currentDate = Date() + private let calendar = Calendar.current + private let dateFormatter = DateFormatter() + private let weekdays = ["일", "월", "화", "수", "목", "금", "토"] + private var emotionData: [Int: String] = [:] // 날짜: 이모지 매핑 + private var selectedDate: Date? // 선택된 날짜 + + // MARK: Initialization + public override init(frame: CGRect) { + super.init(frame: frame) + setupCalendarView() + } + + required public init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + // MARK: Configuration + public func configureSubviews() { + setupWeekdayLabels() + addSubview(weekdayStackView) + addSubview(calendarCollectionView) + calendarCollectionView.delegate = self + calendarCollectionView.dataSource = self + } + + private func setupCalendarView() { + dateFormatter.locale = Locale(identifier: "ko_KR") + dateFormatter.dateFormat = "yyyy년 M월" + } + + private func formatDateToString(_ date: Date) -> String { + let formatter = DateFormatter() + formatter.dateFormat = "yyyy-MM-dd" + return formatter.string(from: date) + } + + private func setupWeekdayLabels() { + for weekday in weekdays { + let label = UILabel().then { + $0.text = weekday + $0.textAlignment = .center + $0.font = UIFont.systemFont(ofSize: 14, weight: .medium) + } + + if weekday == "일" { + label.textColor = .systemRed + } else if weekday == "토" { + label.textColor = .systemBlue + } else { + label.textColor = CommonUIAssets.LMGray3 + } + + weekdayStackView.addArrangedSubview(label) + } + } + + // MARK: Layout + public func makeConstraints() { + weekdayStackView.snp.makeConstraints { + $0.top.leading.trailing.equalToSuperview() + $0.height.equalTo(30) + } + + calendarCollectionView.snp.makeConstraints { + $0.top.equalTo(weekdayStackView.snp.bottom) + $0.leading.trailing.bottom.equalToSuperview() + } + } + + // MARK: Helper Methods + private func getDaysInMonth() -> [Date?] { + guard let range = calendar.range(of: .day, in: .month, for: currentDate), + let firstDayOfMonth = calendar.date(from: calendar.dateComponents([.year, .month], from: currentDate)) else { + return [] + } + + let firstWeekday = calendar.component(.weekday, from: firstDayOfMonth) + let numberOfDays = range.count + let numberOfEmptyDays = firstWeekday - 1 + + var days: [Date?] = Array(repeating: nil, count: numberOfEmptyDays) + + for day in 1...numberOfDays { + if let date = calendar.date(byAdding: .day, value: day - 1, to: firstDayOfMonth) { + days.append(date) + } + } + + return days + } + + // MARK: Public Methods + public func setEmotionData(_ data: [Int: String]) { + emotionData = data + calendarCollectionView.reloadData() + } + + public func setEmotion(for day: Int, emotion: String) { + emotionData[day] = emotion + calendarCollectionView.reloadData() + } + + public func setSelectedDate(_ date: Date?) { + selectedDate = date + calendarCollectionView.reloadData() + } + + public func updateMonth(year: Int, month: Int) { + let calendar = Calendar.current + var components = DateComponents() + components.year = year + components.month = month + components.day = 1 + + if let newDate = calendar.date(from: components) { + currentDate = newDate + calendarCollectionView.reloadData() + } + } +} + +// MARK: - UICollectionViewDataSource +extension CalendarView: UICollectionViewDataSource { + public func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { + return getDaysInMonth().count + } + + public func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { + let cell = collectionView.dequeueReusableCell(withReuseIdentifier: CalendarCell.identifier, for: indexPath) as! CalendarCell + let days = getDaysInMonth() + + if let date = days[indexPath.item] { + let day = calendar.component(.day, from: date) + let isToday = calendar.isDateInToday(date) + let isSunday = calendar.component(.weekday, from: date) == 1 + let emotion = emotionData[day] + let isSelected = selectedDate != nil && calendar.isDate(date, inSameDayAs: selectedDate!) + cell.configure(day: day, isToday: isToday, isSunday: isSunday, emotion: emotion, isSelected: isSelected) + } else { + cell.configure(day: nil, isToday: false, isSunday: false, emotion: nil, isSelected: false) + } + + return cell + } + +} + +// MARK: - UICollectionViewDelegate +extension CalendarView: UICollectionViewDelegate { + public func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { + let days = getDaysInMonth() + if let selectedDate = days[indexPath.item] { + self.selectedDate = selectedDate + calendarCollectionView.reloadData() + tapDay?(selectedDate) + } + } +} + +// MARK: - UICollectionViewDelegateFlowLayout +extension CalendarView: UICollectionViewDelegateFlowLayout { + public func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize { + let width = collectionView.frame.width / 7 + return CGSize(width: width, height: 70) + } + +} + +// MARK: - CalendarCell +public class CalendarCell: UICollectionViewCell { + public static let identifier = "CalendarCell" + + private let dayLabel = UILabel().then { + $0.textAlignment = .center + $0.font = UIFont.systemFont(ofSize: 16, weight: .regular) + $0.textColor = CommonUIAssets.LMGray1 + } + + private let emotionLabel = UILabel().then { + $0.textAlignment = .center + $0.font = UIFont.systemFont(ofSize: 15) + } + + private let stackView = UIStackView().then { + $0.axis = .vertical + $0.alignment = .center + $0.spacing = 10 + } + + private let bottomBorderView = UIView().then { + $0.backgroundColor = CommonUIAssets.LMGray5 + } + + // 선택된 날짜 배경을 위한 뷰 + private let selectionBackgroundView = UIView().then { + $0.backgroundColor = CommonUIAssets.LMOrange3 + $0.layer.cornerRadius = 8 + } + + // 오늘 날짜 동그라미를 위한 뷰 + private let todayCircleView = UIView().then { + $0.backgroundColor = CommonUIAssets.LMOrange1 + $0.layer.cornerRadius = 12 + } + + public override init(frame: CGRect) { + super.init(frame: frame) + setupUI() + } + + required public init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + private func setupUI() { + addSubview(selectionBackgroundView) + addSubview(todayCircleView) + addSubview(stackView) + addSubview(bottomBorderView) + stackView.addArrangedSubview(dayLabel) + stackView.addArrangedSubview(emotionLabel) + + // 선택된 날짜 배경 뷰 레이아웃 + selectionBackgroundView.snp.makeConstraints { + $0.edges.equalToSuperview().inset(5) + } + + // 오늘 날짜 동그라미 뷰 레이아웃 + todayCircleView.snp.makeConstraints { + $0.centerX.equalToSuperview() + $0.centerY.equalTo(dayLabel) + $0.width.height.equalTo(24) + } + + stackView.snp.makeConstraints { + $0.centerX.equalToSuperview() + $0.top.equalToSuperview().offset(10) + $0.bottom.equalToSuperview().offset(-10) + } + + bottomBorderView.snp.makeConstraints { + $0.leading.trailing.bottom.equalToSuperview() + $0.height.equalTo(1) + } + + // 초기에는 숨김 + selectionBackgroundView.isHidden = true + todayCircleView.isHidden = true + } + + public func configure(day: Int?, isToday: Bool, isSunday: Bool, emotion: String?, isSelected: Bool = false) { + if let day = day { + dayLabel.text = "\(day)" + + // 선택된 날짜 배경 표시/숨김 + selectionBackgroundView.isHidden = !isSelected + + // 오늘 날짜 동그라미 표시/숨김 + todayCircleView.isHidden = !isToday + + // 날짜 색상 설정 + if isToday { + // 오늘 날짜는 흰색 텍스트 (동그라미 배경 위에) + dayLabel.textColor = .white + } else if isSunday { + dayLabel.textColor = .systemRed + } else { + dayLabel.textColor = CommonUIAssets.LMGray1 + } + + // 이모지 설정 - 간격 유지를 위해 항상 표시 + if let emotion = emotion, !emotion.isEmpty { + emotionLabel.text = emotion + } else { + emotionLabel.text = " " // 공백으로 간격 유지 + } + emotionLabel.isHidden = false // 항상 표시하여 간격 유지 + } else { + dayLabel.text = "" + emotionLabel.text = "" + emotionLabel.isHidden = true + selectionBackgroundView.isHidden = true + todayCircleView.isHidden = true + } + } +} diff --git a/Projects/CommonUI/Sources/View/Diary/DiaryAddView.swift b/Projects/CommonUI/Sources/View/Diary/DiaryAddView.swift new file mode 100644 index 0000000..8b5494d --- /dev/null +++ b/Projects/CommonUI/Sources/View/Diary/DiaryAddView.swift @@ -0,0 +1,193 @@ +// +// DiaryAddView.swift +// CommonUI +// +// Created by 박지윤 on 9/14/25. +// + +import UIKit +import SnapKit +import Then +import RxSwift + +open class DiaryAddView: UIView { + // MARK: Properties + private let placeholder = """ + 오늘 하루는 어떠셨나요? + 감정을 중심으로 작성하면 + 더욱 정확한 분석이 가능해요. + """ + + // MARK: UI Components + private(set) var dateLabel = UILabel().then { + $0.text = Date().getToday() + $0.textColor = .black + $0.font = UIFont.systemFont(ofSize: 20, weight: .semibold) + } + + private let dateUnderlineView = UIView().then { + $0.backgroundColor = CommonUIAssets.LMOrange1 + } + + private let diaryTextView = UITextView().then { + $0.backgroundColor = CommonUIAssets.LMOrange1?.withAlphaComponent(0.13) + $0.font = UIFont.systemFont(ofSize: 20, weight: .semibold) + $0.layer.cornerRadius = 12 + $0.textContainerInset = UIEdgeInsets(top: 25, left: 25, bottom: 25, right: 25) + } + + private let textNumLabel = UILabel().then { + $0.text = "0 / 500" + $0.font = UIFont.systemFont(ofSize: 15, weight: .light) + $0.textColor = CommonUIAssets.LMGray1 + $0.textAlignment = .right + } + + private var diaryAddButton = LMButton(textColor: CommonUIAssets.LMBlack, + bgColor: CommonUIAssets.LMOrange1) + + let disposeBag = DisposeBag() + + public override init(frame: CGRect) { + super.init(frame: frame) + configureSubviews() + makeConstraints() + bindEvents() + } + + required public init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + // MARK: Properties + public var onAddButtonTapped: ((String) -> Void)? + + // MARK: Configuration + func configureSubviews() { + backgroundColor = .white + setTextView() + + diaryAddButton = diaryAddButton.then() { + $0.setTitle("일기 작성하기", for: .normal) + } + + addSubview(dateLabel) + addSubview(dateUnderlineView) + addSubview(diaryTextView) + addSubview(textNumLabel) + addSubview(diaryAddButton) + } + + // MARK: Layout + func makeConstraints() { + dateLabel.snp.makeConstraints { + $0.top.equalTo(safeAreaLayoutGuide).inset(20) + $0.leading.equalToSuperview().inset(20) + } + + dateUnderlineView.snp.makeConstraints { + $0.top.equalTo(dateLabel.snp.bottom) + $0.centerX.width.equalTo(dateLabel) + $0.height.equalTo(3) + } + + diaryTextView.snp.makeConstraints { + $0.top.equalTo(dateUnderlineView.snp.bottom).offset(17) + $0.horizontalEdges.equalToSuperview().inset(20) + $0.width.equalToSuperview().inset(20) + $0.bottom.equalTo(diaryAddButton.snp.top).offset(-30) + } + + textNumLabel.snp.makeConstraints { + $0.trailing.bottom.equalTo(diaryTextView).inset(25) + } + + diaryAddButton.snp.makeConstraints { + $0.width.equalToSuperview().inset(20) + $0.centerX.equalToSuperview() + $0.bottom.equalTo(safeAreaLayoutGuide).inset(15) + } + } + + func bindEvents() { + diaryAddButton.rx.tap + .subscribe(onNext: { [weak self] in + guard let self = self else { return } + let content = self.getDiaryText() ?? "" + self.onAddButtonTapped?(content) + }) + .disposed(by: disposeBag) + } +} + +extension DiaryAddView: UITextViewDelegate { + private func setTextView() { + diaryTextView.delegate = self + let paragraphStyle = NSMutableParagraphStyle() + paragraphStyle.lineSpacing = 4 + + let attributes: [NSAttributedString.Key: Any] = [ + .font: UIFont.systemFont(ofSize: 15, weight: .light), + .foregroundColor: CommonUIAssets.LMGray1 ?? .gray, + .paragraphStyle: paragraphStyle + ] + + let attributedString = NSAttributedString(string: placeholder, attributes: attributes) + + diaryTextView.attributedText = attributedString + } + + public func textViewDidChange(_ textView: UITextView) { + if diaryTextView.text.count > 500 { + diaryTextView.deleteBackward() + } + + textNumLabel.text = "\(diaryTextView.text.count) / 500" + + let attributedString = NSMutableAttributedString(string: "\(diaryTextView.text.count) / 500") + attributedString.addAttribute(.foregroundColor, value: CommonUIAssets.LMOrange1 ?? .orange, range: ("\(diaryTextView.text.count) / 500" as NSString).range(of:"\(diaryTextView.text.count)")) + textNumLabel.attributedText = attributedString + } + + public func textViewDidBeginEditing(_ textView: UITextView) { + if diaryTextView.text.isEmpty { + let paragraphStyle = NSMutableParagraphStyle() + paragraphStyle.lineSpacing = 4 + + let attributes: [NSAttributedString.Key: Any] = [ + .font: UIFont.systemFont(ofSize: 15, weight: .light), + .foregroundColor: CommonUIAssets.LMGray1 ?? .gray, + .paragraphStyle: paragraphStyle + ] + + let attributedString = NSAttributedString(string: placeholder, attributes: attributes) + + diaryTextView.attributedText = attributedString + } else if diaryTextView.text == placeholder { + diaryTextView.textColor = .black + diaryTextView.text = nil + } + } + + public func textViewDidEndEditing(_ textView: UITextView) { + if diaryTextView.text.isEmpty { + let paragraphStyle = NSMutableParagraphStyle() + paragraphStyle.lineSpacing = 4 + + let attributes: [NSAttributedString.Key: Any] = [ + .font: UIFont.systemFont(ofSize: 15, weight: .light), + .foregroundColor: CommonUIAssets.LMGray1 ?? .gray, + .paragraphStyle: paragraphStyle + ] + + let attributedString = NSAttributedString(string: placeholder, attributes: attributes) + + diaryTextView.attributedText = attributedString + } + } + + func getDiaryText() -> String? { + let text = diaryTextView.text + return text == placeholder ? nil : text + } +} diff --git a/Projects/CommonUI/Sources/View/Diary/DiaryDetailView.swift b/Projects/CommonUI/Sources/View/Diary/DiaryDetailView.swift new file mode 100644 index 0000000..f27a100 --- /dev/null +++ b/Projects/CommonUI/Sources/View/Diary/DiaryDetailView.swift @@ -0,0 +1,447 @@ +// +// DiaryDetailView.swift +// CommonUI +// +// Created by 박지윤 on 9/20/25. +// + +import UIKit +import SnapKit +import Then +import Domain +import RxSwift + +open class DiaryDetailView: UIView { + + // MARK: UI Components + private let scrollView = UIScrollView().then { + $0.showsVerticalScrollIndicator = true + $0.showsHorizontalScrollIndicator = true + } + + private let contentView = UIView() + + private(set) var dateLabel = UILabel().then { + $0.textColor = .black + $0.font = UIFont.systemFont(ofSize: 20, weight: .semibold) + } + + private let dateUnderlineView = UIView().then { + $0.backgroundColor = CommonUIAssets.LMOrange1 + } + + // 원형 점수 표시기 + private let scoreContainerView = UIView() + private let scoreCircleView = UIView().then { + $0.backgroundColor = .yellow + $0.layer.borderWidth = 8 + $0.layer.borderColor = CommonUIAssets.LMOrange1?.cgColor + $0.layer.cornerRadius = 60 + } + + private let scoreProgressView = UIView().then { + $0.backgroundColor = CommonUIAssets.LMOrange3 + $0.layer.cornerRadius = 60 + } + + private let scoreLabel = UILabel().then { + $0.font = UIFont.systemFont(ofSize: 24, weight: .bold) + $0.textColor = CommonUIAssets.LMBlack + $0.textAlignment = .center + } + + // 내가 쓴 일기 섹션 + private let originalSectionTitleLabel = UILabel().then { + $0.text = "내가 쓴 일기" + $0.font = UIFont.systemFont(ofSize: 16, weight: .semibold) + $0.textColor = CommonUIAssets.LMBlack + } + + private let originalContentContainer = UIView().then { + $0.backgroundColor = CommonUIAssets.LMOrange1?.withAlphaComponent(0.1) + $0.layer.cornerRadius = 12 + } + + private let originalContentLabel = UILabel().then { + $0.font = UIFont.systemFont(ofSize: 16, weight: .regular) + $0.textColor = CommonUIAssets.LMBlack + $0.numberOfLines = 0 + } + + // 화살표 + private let arrowImageView = UIImageView().then { + $0.image = UIImage(systemName: "arrow.down") + $0.tintColor = CommonUIAssets.LMGray3 + $0.contentMode = .scaleAspectFit + } + + // 맞춤법 교정 결과 섹션 + private let revisedSectionTitleLabel = UILabel().then { + $0.text = "맞춤법 교정 결과" + $0.font = UIFont.systemFont(ofSize: 16, weight: .semibold) + $0.textColor = CommonUIAssets.LMBlack + } + + private let revisedContentContainer = UIView().then { + $0.backgroundColor = CommonUIAssets.LMOrange1?.withAlphaComponent(0.1) + $0.layer.cornerRadius = 12 + } + + private let revisedContentLabel = UILabel().then { + $0.font = UIFont.systemFont(ofSize: 16, weight: .regular) + $0.textColor = CommonUIAssets.LMBlack + $0.numberOfLines = 0 + } + + // 맞춤법 오류 섹션 + private let errorSectionTitleLabel = UILabel().then { + $0.font = UIFont.systemFont(ofSize: 16, weight: .semibold) + $0.textColor = CommonUIAssets.LMBlack + } + + private let errorStackView = UIStackView().then { + $0.axis = .vertical + $0.spacing = 12 + $0.distribution = .fill + } + + // AI 피드백 섹션 + private let feedbackSectionTitleLabel = UILabel().then { + $0.text = "AI 피드백" + $0.font = UIFont.systemFont(ofSize: 16, weight: .semibold) + $0.textColor = CommonUIAssets.LMBlack + } + + private let feedbackContainer = UIView().then { + $0.backgroundColor = CommonUIAssets.LMOrange1?.withAlphaComponent(0.1) + $0.layer.cornerRadius = 12 + } + + private let feedbackLabel = UILabel().then { + $0.font = UIFont.systemFont(ofSize: 16, weight: .regular) + $0.textColor = CommonUIAssets.LMBlack + $0.numberOfLines = 0 + } + + // MARK: Properties + private var diaryData: DiaryVO? + let disposeBag = DisposeBag() + + // MARK: Public Properties + public var onSaveButtonTapped: (() -> Void)? + + public override init(frame: CGRect) { + super.init(frame: frame) + setupUI() + bindEvents() + } + + required public init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + // MARK: Setup + private func setupUI() { + backgroundColor = CommonUIAssets.LMWhite + + addSubview(scrollView) + scrollView.addSubview(contentView) + + // 원형 점수 표시기 설정 + scoreContainerView.addSubview(scoreCircleView) + scoreContainerView.addSubview(scoreProgressView) + scoreContainerView.addSubview(scoreLabel) + + // 원문 컨테이너 설정 + originalContentContainer.addSubview(originalContentLabel) + + // 수정문 컨테이너 설정 + revisedContentContainer.addSubview(revisedContentLabel) + + // 피드백 컨테이너 설정 + feedbackContainer.addSubview(feedbackLabel) + + [dateLabel, dateUnderlineView, scoreContainerView, + originalSectionTitleLabel, originalContentContainer, + arrowImageView, revisedSectionTitleLabel, revisedContentContainer, + errorSectionTitleLabel, errorStackView, + feedbackSectionTitleLabel, feedbackContainer] + .forEach { contentView.addSubview($0) } + + setupConstraints() + } + + private func bindEvents() { + } + + private func setupConstraints() { + scrollView.snp.makeConstraints { + $0.edges.equalToSuperview() + } + + contentView.snp.makeConstraints { + $0.edges.equalToSuperview() + $0.width.equalToSuperview() + } + + dateLabel.snp.makeConstraints { + $0.top.equalToSuperview().inset(20) + $0.leading.equalToSuperview().inset(20) + } + + dateUnderlineView.snp.makeConstraints { + $0.top.equalTo(dateLabel.snp.bottom) + $0.centerX.width.equalTo(dateLabel) + $0.height.equalTo(3) + } + + scoreContainerView.snp.makeConstraints { + $0.top.equalTo(dateUnderlineView.snp.bottom).offset(30) + $0.centerX.equalToSuperview() + $0.width.height.equalTo(120) + } + + scoreCircleView.snp.makeConstraints { + $0.edges.equalToSuperview() + } + + scoreProgressView.snp.makeConstraints { + $0.edges.equalToSuperview() + } + + scoreLabel.snp.makeConstraints { + $0.center.equalToSuperview() + } + + // 내가 쓴 일기 섹션 + originalSectionTitleLabel.snp.makeConstraints { + $0.top.equalTo(scoreContainerView.snp.bottom).offset(30) + $0.leading.trailing.equalToSuperview().inset(20) + } + + originalContentContainer.snp.makeConstraints { + $0.top.equalTo(originalSectionTitleLabel.snp.bottom).offset(12) + $0.leading.trailing.equalToSuperview().inset(20) + } + + originalContentLabel.snp.makeConstraints { + $0.edges.equalToSuperview().inset(16) + } + + // 화살표 + arrowImageView.snp.makeConstraints { + $0.top.equalTo(originalContentContainer.snp.bottom).offset(16) + $0.centerX.equalToSuperview() + $0.width.height.equalTo(20) + } + + // 맞춤법 교정 결과 섹션 + revisedSectionTitleLabel.snp.makeConstraints { + $0.top.equalTo(arrowImageView.snp.bottom).offset(16) + $0.leading.trailing.equalToSuperview().inset(20) + } + + revisedContentContainer.snp.makeConstraints { + $0.top.equalTo(revisedSectionTitleLabel.snp.bottom).offset(12) + $0.leading.trailing.equalToSuperview().inset(20) + } + + revisedContentLabel.snp.makeConstraints { + $0.edges.equalToSuperview().inset(16) + } + + // 맞춤법 오류 섹션 + errorSectionTitleLabel.snp.makeConstraints { + $0.top.equalTo(revisedContentContainer.snp.bottom).offset(20) + $0.leading.trailing.equalToSuperview().inset(20) + } + + errorStackView.snp.makeConstraints { + $0.top.equalTo(errorSectionTitleLabel.snp.bottom).offset(12) + $0.leading.trailing.equalToSuperview().inset(20) + } + + // AI 피드백 섹션 + feedbackSectionTitleLabel.snp.makeConstraints { + $0.top.equalTo(errorStackView.snp.bottom).offset(20) + $0.leading.trailing.equalToSuperview().inset(20) + } + + feedbackContainer.snp.makeConstraints { + $0.top.equalTo(feedbackSectionTitleLabel.snp.bottom).offset(12) + $0.leading.trailing.equalToSuperview().inset(20) + $0.bottom.equalToSuperview().inset(20) + } + + feedbackLabel.snp.makeConstraints { + $0.edges.equalToSuperview().inset(16) + } + } + + // MARK: Public Methods + public func configure(with diary: DiaryVO) { + self.diaryData = diary + + // 날짜 설정 + dateLabel.text = diary.createdAt + + // 원문 설정 + originalContentLabel.text = diary.originContent + + // 수정문 설정 (하이라이트 포함) + setupRevisedContent(diary.spellingDto.revisedContent, revisions: diary.spellingDto.revisions) + + // 점수 설정 + scoreLabel.text = "\(diary.spellingDto.score)점" + setupScoreProgress(score: diary.spellingDto.score) + + // 오류 섹션 설정 + setupErrorSection(revisions: diary.spellingDto.revisions) + + // 피드백 설정 + feedbackLabel.text = diary.feedback + } + + private func setupScoreProgress(score: Int) { + let progress = CGFloat(score) / 100.0 + let angle = progress * 2 * .pi - .pi / 2 // -90도부터 시작 + + // 원형 프로그레스 애니메이션 + let path = UIBezierPath(arcCenter: CGPoint(x: 60, y: 60), + radius: 52, + startAngle: -.pi / 2, + endAngle: angle, + clockwise: true) + + let shapeLayer = CAShapeLayer() + shapeLayer.path = path.cgPath + shapeLayer.fillColor = UIColor.clear.cgColor + shapeLayer.strokeColor = CommonUIAssets.LMOrange1?.cgColor + shapeLayer.lineWidth = 8 + shapeLayer.lineCap = .round + + scoreProgressView.layer.sublayers?.removeAll() + scoreProgressView.layer.addSublayer(shapeLayer) + } + + private func setupRevisedContent(_ revisedContent: String, revisions: [RevisionVO]) { + let attributedString = NSMutableAttributedString(string: revisedContent) + + for revision in revisions { + let range = (revisedContent as NSString).range(of: revision.revisedContent) + if range.location != NSNotFound { + attributedString.addAttribute(.backgroundColor, + value: CommonUIAssets.LMOrange1?.withAlphaComponent(0.3) ?? UIColor.orange.withAlphaComponent(0.3), + range: range) + } + } + + revisedContentLabel.attributedText = attributedString + } + + private func setupErrorSection(revisions: [RevisionVO]) { + errorStackView.arrangedSubviews.forEach { $0.removeFromSuperview() } + + if revisions.isEmpty { + errorSectionTitleLabel.text = "맞춤법 오류 0개" + let noErrorsLabel = UILabel().then { + $0.text = "완벽한 문장이에요! 🎉" + $0.font = UIFont.systemFont(ofSize: 16, weight: .medium) + $0.textColor = CommonUIAssets.LMOrange1 + $0.textAlignment = .center + } + errorStackView.addArrangedSubview(noErrorsLabel) + } else { + errorSectionTitleLabel.text = "맞춤법 오류 \(revisions.count)개" + + for revision in revisions { + let errorView = createErrorView(revision) + errorStackView.addArrangedSubview(errorView) + } + } + } + + private func createErrorView(_ revision: RevisionVO) -> UIView { + let containerView = UIView() + + let originalBox = UIView().then { + $0.backgroundColor = .clear + $0.layer.borderWidth = 1 + $0.layer.borderColor = CommonUIAssets.LMOrange1?.withAlphaComponent(0.3).cgColor + $0.layer.cornerRadius = 8 + } + + let originalLabel = UILabel().then { + $0.text = revision.originContent + $0.font = UIFont.systemFont(ofSize: 14, weight: .medium) + $0.textColor = CommonUIAssets.LMBlack + $0.textAlignment = .center + } + + let arrowImageView = UIImageView().then { + $0.image = UIImage(systemName: "arrow.right") + $0.tintColor = CommonUIAssets.LMOrange1 + $0.contentMode = .scaleAspectFit + } + + let revisedBox = UIView().then { + $0.backgroundColor = CommonUIAssets.LMOrange1?.withAlphaComponent(0.3) + $0.layer.cornerRadius = 8 + } + + let revisedLabel = UILabel().then { + $0.text = revision.revisedContent + $0.font = UIFont.systemFont(ofSize: 14, weight: .medium) + $0.textColor = CommonUIAssets.LMBlack + $0.textAlignment = .center + } + + [originalBox, originalLabel, arrowImageView, revisedBox, revisedLabel] + .forEach { containerView.addSubview($0) } + + originalBox.snp.makeConstraints { + $0.leading.equalToSuperview() + $0.centerY.equalToSuperview() + $0.height.equalTo(32) + } + + originalLabel.snp.makeConstraints { + $0.edges.equalTo(originalBox).inset(8) + } + + // originalBox의 너비를 originalLabel의 내용에 따라 동적으로 설정 + originalBox.snp.makeConstraints { + $0.width.greaterThanOrEqualTo(60) // 최소 너비 + $0.width.lessThanOrEqualTo(120) // 최대 너비 + } + + arrowImageView.snp.makeConstraints { + $0.leading.equalTo(originalBox.snp.trailing).offset(8) + $0.centerY.equalToSuperview() + $0.width.height.equalTo(16) + } + + revisedBox.snp.makeConstraints { + $0.leading.equalTo(arrowImageView.snp.trailing).offset(8) + $0.centerY.equalToSuperview() + $0.height.equalTo(32) + } + + revisedLabel.snp.makeConstraints { + $0.edges.equalTo(revisedBox).inset(8) + } + + // revisedBox의 너비를 revisedLabel의 내용에 따라 동적으로 설정 + revisedBox.snp.makeConstraints { + $0.width.greaterThanOrEqualTo(60) // 최소 너비 + $0.width.lessThanOrEqualTo(120) // 최대 너비 + } + + containerView.snp.makeConstraints { + $0.height.equalTo(32) + } + + return containerView + } + +} diff --git a/Projects/CommonUI/Sources/View/Diary/DiaryEditView.swift b/Projects/CommonUI/Sources/View/Diary/DiaryEditView.swift new file mode 100644 index 0000000..8701dc7 --- /dev/null +++ b/Projects/CommonUI/Sources/View/Diary/DiaryEditView.swift @@ -0,0 +1,13 @@ +// +// DiaryEditView.swift +// CommonUI +// +// Created by 박지윤 on 9/14/25. +// + +import UIKit +import SnapKit +import Then + +open class DiaryEditView: UIView { +} diff --git a/Projects/CommonUI/Sources/View/Diary/DiaryLoadingView.swift b/Projects/CommonUI/Sources/View/Diary/DiaryLoadingView.swift new file mode 100644 index 0000000..1b05880 --- /dev/null +++ b/Projects/CommonUI/Sources/View/Diary/DiaryLoadingView.swift @@ -0,0 +1,61 @@ +// +// DiaryLoadingView.swift +// CommonUI +// +// Created by 박지윤 on 9/14/25. +// + +import UIKit +import SnapKit +import Then + +open class DiaryLoadingView: UIView { + private let loadingSpinner = UIActivityIndicatorView(style: .large).then { + $0.color = CommonUIAssets.LMGray1 + $0.startAnimating() + } + + private let mainLabel = UILabel().then { + $0.text = "사용자 대화를 분석 중입니다..." + $0.textColor = CommonUIAssets.LMGray1 + $0.font = UIFont.systemFont(ofSize: 18, weight: .medium) + $0.textAlignment = .center + } + + private let subLabel = UILabel().then { + $0.text = "잠시만 기다려 주세요" + $0.textColor = CommonUIAssets.LMGray3 + $0.font = UIFont.systemFont(ofSize: 14, weight: .regular) + $0.textAlignment = .center + } + + public override init(frame: CGRect) { + super.init(frame: frame) + setupUI() + } + + required public init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + private func setupUI() { + backgroundColor = CommonUIAssets.LMOrange4 + + [loadingSpinner, mainLabel, subLabel].forEach { addSubview($0) } + + loadingSpinner.snp.makeConstraints { + $0.centerX.equalToSuperview() + $0.centerY.equalToSuperview().offset(-40) + } + + mainLabel.snp.makeConstraints { + $0.centerX.equalToSuperview() + $0.top.equalTo(loadingSpinner.snp.bottom).offset(20) + } + + subLabel.snp.makeConstraints { + $0.centerX.equalToSuperview() + $0.top.equalTo(mainLabel.snp.bottom).offset(8) + } + } +} diff --git a/Projects/CommonUI/Sources/View/Diary/DiaryResultView.swift b/Projects/CommonUI/Sources/View/Diary/DiaryResultView.swift new file mode 100644 index 0000000..a0559f3 --- /dev/null +++ b/Projects/CommonUI/Sources/View/Diary/DiaryResultView.swift @@ -0,0 +1,522 @@ +// +// DiaryResultView.swift +// CommonUI +// +// Created by 박지윤 on 1/7/25. +// + +import UIKit +import SnapKit +import Then +import Domain +import RxSwift + +open class DiaryResultView: UIView { + + // MARK: UI Components + private let scrollView = UIScrollView().then { + $0.showsVerticalScrollIndicator = true + $0.showsHorizontalScrollIndicator = true + } + + private let contentView = UIView() + + private(set) var dateLabel = UILabel().then { + $0.text = Date().getToday() + $0.textColor = .black + $0.font = UIFont.systemFont(ofSize: 20, weight: .semibold) + } + + private let dateUnderlineView = UIView().then { + $0.backgroundColor = CommonUIAssets.LMOrange1 + } + + // 원형 점수 표시기 + private let scoreContainerView = UIView() + private let scoreCircleView = UIView().then { + $0.backgroundColor = .yellow + $0.layer.borderWidth = 8 + $0.layer.borderColor = CommonUIAssets.LMOrange1?.cgColor + $0.layer.cornerRadius = 60 + } + + private let scoreProgressView = UIView().then { + $0.backgroundColor = CommonUIAssets.LMOrange3 + $0.layer.cornerRadius = 60 + } + + private let scoreLabel = UILabel().then { + $0.font = UIFont.systemFont(ofSize: 24, weight: .bold) + $0.textColor = CommonUIAssets.LMBlack + $0.textAlignment = .center + } + + // 내가 쓴 일기 섹션 + private let originalSectionTitleLabel = UILabel().then { + $0.text = "내가 쓴 일기" + $0.font = UIFont.systemFont(ofSize: 16, weight: .semibold) + $0.textColor = CommonUIAssets.LMBlack + } + + private let originalContentContainer = UIView().then { + $0.backgroundColor = CommonUIAssets.LMOrange1?.withAlphaComponent(0.1) + $0.layer.cornerRadius = 12 + } + + private let originalContentLabel = UILabel().then { + $0.font = UIFont.systemFont(ofSize: 16, weight: .regular) + $0.textColor = CommonUIAssets.LMBlack + $0.numberOfLines = 0 + } + + // 화살표 + private let arrowImageView = UIImageView().then { + $0.image = UIImage(systemName: "arrow.down") + $0.tintColor = CommonUIAssets.LMGray3 + $0.contentMode = .scaleAspectFit + } + + // 맞춤법 교정 결과 섹션 + private let revisedSectionTitleLabel = UILabel().then { + $0.text = "맞춤법 교정 결과" + $0.font = UIFont.systemFont(ofSize: 16, weight: .semibold) + $0.textColor = CommonUIAssets.LMBlack + } + + private let revisedContentContainer = UIView().then { + $0.backgroundColor = CommonUIAssets.LMOrange1?.withAlphaComponent(0.1) + $0.layer.cornerRadius = 12 + } + + private let revisedContentLabel = UILabel().then { + $0.font = UIFont.systemFont(ofSize: 16, weight: .regular) + $0.textColor = CommonUIAssets.LMBlack + $0.numberOfLines = 0 + } + + // 맞춤법 오류 섹션 + private let errorSectionTitleLabel = UILabel().then { + $0.font = UIFont.systemFont(ofSize: 16, weight: .semibold) + $0.textColor = CommonUIAssets.LMBlack + } + + private let errorStackView = UIStackView().then { + $0.axis = .vertical + $0.spacing = 12 + $0.distribution = .fill + } + + // AI 피드백 섹션 + private let feedbackSectionTitleLabel = UILabel().then { + $0.text = "AI 피드백" + $0.font = UIFont.systemFont(ofSize: 16, weight: .semibold) + $0.textColor = CommonUIAssets.LMBlack + } + + private let feedbackContainer = UIView().then { + $0.backgroundColor = CommonUIAssets.LMOrange1?.withAlphaComponent(0.1) + $0.layer.cornerRadius = 12 + } + + private let feedbackLabel = UILabel().then { + $0.font = UIFont.systemFont(ofSize: 16, weight: .regular) + $0.textColor = CommonUIAssets.LMBlack + $0.numberOfLines = 0 + } + + private var confirmButton = LMButton(textColor: CommonUIAssets.LMBlack, + bgColor: CommonUIAssets.LMOrange1) + + // MARK: Properties + private var diaryData: DiaryVO? + private var isButtonVisible: Bool = true + let disposeBag = DisposeBag() + + // MARK: Public Properties + public var onSaveButtonTapped: (() -> Void)? + + public override init(frame: CGRect) { + super.init(frame: frame) + setupUI() + bindEvents() + } + + public convenience init(frame: CGRect, showButton: Bool) { + self.init(frame: frame) + self.isButtonVisible = showButton + updateButtonVisibility() + } + + required public init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + // MARK: Setup + private func setupUI() { + backgroundColor = CommonUIAssets.LMWhite + + // 버튼 설정 + confirmButton = confirmButton.then { + $0.setTitle("일기 저장하기", for: .normal) + $0.layer.cornerRadius = 12 + } + + addSubview(scrollView) + if isButtonVisible { + addSubview(confirmButton) + } + scrollView.addSubview(contentView) + + // 원형 점수 표시기 설정 + scoreContainerView.addSubview(scoreCircleView) + scoreContainerView.addSubview(scoreProgressView) + scoreContainerView.addSubview(scoreLabel) + + // 원문 컨테이너 설정 + originalContentContainer.addSubview(originalContentLabel) + + // 수정문 컨테이너 설정 + revisedContentContainer.addSubview(revisedContentLabel) + + // 피드백 컨테이너 설정 + feedbackContainer.addSubview(feedbackLabel) + + [dateLabel, dateUnderlineView, scoreContainerView, + originalSectionTitleLabel, originalContentContainer, + arrowImageView, revisedSectionTitleLabel, revisedContentContainer, + errorSectionTitleLabel, errorStackView, + feedbackSectionTitleLabel, feedbackContainer] + .forEach { contentView.addSubview($0) } + + setupConstraints() + } + + private func bindEvents() { + confirmButton.rx.tap + .subscribe(onNext: { [weak self] in + self?.onSaveButtonTapped?() + }) + .disposed(by: disposeBag) + } + + private func setupConstraints() { + if isButtonVisible { + scrollView.snp.makeConstraints { + $0.top.leading.trailing.equalToSuperview() + $0.bottom.equalTo(confirmButton.snp.top).offset(-20) + } + + contentView.snp.makeConstraints { + $0.top.leading.trailing.bottom.equalToSuperview() + $0.width.equalToSuperview() + } + + confirmButton.snp.makeConstraints { + $0.leading.trailing.equalToSuperview().inset(20) + $0.bottom.equalTo(safeAreaLayoutGuide).offset(-20) + } + } else { + scrollView.snp.makeConstraints { + $0.edges.equalToSuperview() + } + + contentView.snp.makeConstraints { + $0.top.leading.trailing.equalToSuperview() + $0.bottom.greaterThanOrEqualToSuperview() + $0.width.equalToSuperview() + } + } + + dateLabel.snp.makeConstraints { + $0.top.equalTo(safeAreaLayoutGuide).inset(20) + $0.leading.equalToSuperview().inset(20) + } + + dateUnderlineView.snp.makeConstraints { + $0.top.equalTo(dateLabel.snp.bottom) + $0.centerX.width.equalTo(dateLabel) + $0.height.equalTo(3) + } + + scoreContainerView.snp.makeConstraints { + $0.top.equalTo(dateUnderlineView.snp.bottom).offset(30) + $0.centerX.equalToSuperview() + $0.width.height.equalTo(120) + } + + scoreCircleView.snp.makeConstraints { + $0.edges.equalToSuperview() + } + + scoreProgressView.snp.makeConstraints { + $0.edges.equalToSuperview() + } + + scoreLabel.snp.makeConstraints { + $0.center.equalToSuperview() + } + + // 내가 쓴 일기 섹션 + originalSectionTitleLabel.snp.makeConstraints { + $0.top.equalTo(scoreContainerView.snp.bottom).offset(30) + $0.leading.trailing.equalToSuperview().inset(20) + } + + originalContentContainer.snp.makeConstraints { + $0.top.equalTo(originalSectionTitleLabel.snp.bottom).offset(12) + $0.leading.trailing.equalToSuperview().inset(20) + } + + originalContentLabel.snp.makeConstraints { + $0.edges.equalToSuperview().inset(16) + } + + // 화살표 + arrowImageView.snp.makeConstraints { + $0.top.equalTo(originalContentContainer.snp.bottom).offset(16) + $0.centerX.equalToSuperview() + $0.width.height.equalTo(20) + } + + // 맞춤법 교정 결과 섹션 + revisedSectionTitleLabel.snp.makeConstraints { + $0.top.equalTo(arrowImageView.snp.bottom).offset(16) + $0.leading.trailing.equalToSuperview().inset(20) + } + + revisedContentContainer.snp.makeConstraints { + $0.top.equalTo(revisedSectionTitleLabel.snp.bottom).offset(12) + $0.leading.trailing.equalToSuperview().inset(20) + } + + revisedContentLabel.snp.makeConstraints { + $0.edges.equalToSuperview().inset(16) + } + + // 맞춤법 오류 섹션 + errorSectionTitleLabel.snp.makeConstraints { + $0.top.equalTo(revisedContentContainer.snp.bottom).offset(20) + $0.leading.trailing.equalToSuperview().inset(20) + } + + errorStackView.snp.makeConstraints { + $0.top.equalTo(errorSectionTitleLabel.snp.bottom).offset(12) + $0.leading.trailing.equalToSuperview().inset(20) + } + + // AI 피드백 섹션 + feedbackSectionTitleLabel.snp.makeConstraints { + $0.top.equalTo(errorStackView.snp.bottom).offset(20) + $0.leading.trailing.equalToSuperview().inset(20) + } + + feedbackContainer.snp.makeConstraints { + $0.top.equalTo(feedbackSectionTitleLabel.snp.bottom).offset(12) + $0.leading.trailing.equalToSuperview().inset(20) + $0.bottom.equalToSuperview().inset(20) + } + + feedbackLabel.snp.makeConstraints { + $0.edges.equalToSuperview().inset(16) + } + } + + // MARK: Public Methods + public func configure(with diary: DiaryVO) { + self.diaryData = diary + + // 날짜 설정 + dateLabel.text = diary.createdAt + + // 원문 설정 + originalContentLabel.text = diary.originContent + + // 수정문 설정 (하이라이트 포함) + setupRevisedContent(diary.spellingDto.revisedContent, revisions: diary.spellingDto.revisions) + + // 점수 설정 + scoreLabel.text = "\(diary.spellingDto.score)점" + setupScoreProgress(score: diary.spellingDto.score) + + // 오류 섹션 설정 + setupErrorSection(revisions: diary.spellingDto.revisions) + + // 피드백 설정 + feedbackLabel.text = diary.feedback + } + + public func setButtonVisibility(_ isVisible: Bool) { + self.isButtonVisible = isVisible + updateButtonVisibility() + } + + private func updateButtonVisibility() { + if isButtonVisible { + if confirmButton.superview == nil { + addSubview(confirmButton) + confirmButton.snp.makeConstraints { + $0.leading.trailing.equalToSuperview().inset(20) + $0.bottom.equalTo(safeAreaLayoutGuide).offset(-20) + } + } + confirmButton.isHidden = false + + // 스크롤뷰 제약조건 업데이트 + scrollView.snp.remakeConstraints { + $0.top.leading.trailing.equalToSuperview() + $0.bottom.equalTo(confirmButton.snp.top).offset(-20) + } + } else { + confirmButton.isHidden = true + confirmButton.removeFromSuperview() + + // 스크롤뷰 제약조건 업데이트 + scrollView.snp.remakeConstraints { + $0.edges.equalToSuperview() + } + } + } + + private func setupScoreProgress(score: Int) { + let progress = CGFloat(score) / 100.0 + let angle = progress * 2 * .pi - .pi / 2 // -90도부터 시작 + + // 원형 프로그레스 애니메이션 + let path = UIBezierPath(arcCenter: CGPoint(x: 60, y: 60), + radius: 52, + startAngle: -.pi / 2, + endAngle: angle, + clockwise: true) + + let shapeLayer = CAShapeLayer() + shapeLayer.path = path.cgPath + shapeLayer.fillColor = UIColor.clear.cgColor + shapeLayer.strokeColor = CommonUIAssets.LMOrange1?.cgColor + shapeLayer.lineWidth = 8 + shapeLayer.lineCap = .round + + scoreProgressView.layer.sublayers?.removeAll() + scoreProgressView.layer.addSublayer(shapeLayer) + } + + private func setupRevisedContent(_ revisedContent: String, revisions: [RevisionVO]) { + let attributedString = NSMutableAttributedString(string: revisedContent) + + for revision in revisions { + let range = (revisedContent as NSString).range(of: revision.revisedContent) + if range.location != NSNotFound { + attributedString.addAttribute(.backgroundColor, + value: CommonUIAssets.LMOrange1?.withAlphaComponent(0.3) ?? UIColor.orange.withAlphaComponent(0.3), + range: range) + } + } + + revisedContentLabel.attributedText = attributedString + } + + private func setupErrorSection(revisions: [RevisionVO]) { + errorStackView.arrangedSubviews.forEach { $0.removeFromSuperview() } + + if revisions.isEmpty { + errorSectionTitleLabel.text = "맞춤법 오류 0개" + let noErrorsLabel = UILabel().then { + $0.text = "완벽한 문장이에요! 🎉" + $0.font = UIFont.systemFont(ofSize: 16, weight: .medium) + $0.textColor = CommonUIAssets.LMOrange1 + $0.textAlignment = .center + } + errorStackView.addArrangedSubview(noErrorsLabel) + } else { + errorSectionTitleLabel.text = "맞춤법 오류 \(revisions.count)개" + + for revision in revisions { + let errorView = createErrorView(revision) + errorStackView.addArrangedSubview(errorView) + } + } + } + + private func createErrorView(_ revision: RevisionVO) -> UIView { + let containerView = UIView() + + let originalBox = UIView().then { + $0.backgroundColor = .clear + $0.layer.borderWidth = 1 + $0.layer.borderColor = CommonUIAssets.LMOrange1?.withAlphaComponent(0.3).cgColor + $0.layer.cornerRadius = 8 + } + + let originalLabel = UILabel().then { + $0.text = revision.originContent + $0.font = UIFont.systemFont(ofSize: 14, weight: .medium) + $0.textColor = CommonUIAssets.LMBlack + $0.textAlignment = .center + } + + let arrowImageView = UIImageView().then { + $0.image = UIImage(systemName: "arrow.right") + $0.tintColor = CommonUIAssets.LMOrange1 + $0.contentMode = .scaleAspectFit + } + + let revisedBox = UIView().then { + $0.backgroundColor = CommonUIAssets.LMOrange1?.withAlphaComponent(0.3) + $0.layer.cornerRadius = 8 + } + + let revisedLabel = UILabel().then { + $0.text = revision.revisedContent + $0.font = UIFont.systemFont(ofSize: 14, weight: .medium) + $0.textColor = CommonUIAssets.LMBlack + $0.textAlignment = .center + } + + [originalBox, originalLabel, arrowImageView, revisedBox, revisedLabel] + .forEach { containerView.addSubview($0) } + + originalBox.snp.makeConstraints { + $0.leading.equalToSuperview() + $0.centerY.equalToSuperview() + $0.height.equalTo(32) + } + + originalLabel.snp.makeConstraints { + $0.edges.equalTo(originalBox).inset(8) + } + + // originalBox의 너비를 originalLabel의 내용에 따라 동적으로 설정 + originalBox.snp.makeConstraints { + $0.width.greaterThanOrEqualTo(60) // 최소 너비 + $0.width.lessThanOrEqualTo(120) // 최대 너비 + } + + arrowImageView.snp.makeConstraints { + $0.leading.equalTo(originalBox.snp.trailing).offset(8) + $0.centerY.equalToSuperview() + $0.width.height.equalTo(16) + } + + revisedBox.snp.makeConstraints { + $0.leading.equalTo(arrowImageView.snp.trailing).offset(8) + $0.centerY.equalToSuperview() + $0.height.equalTo(32) + } + + revisedLabel.snp.makeConstraints { + $0.edges.equalTo(revisedBox).inset(8) + } + + // revisedBox의 너비를 revisedLabel의 내용에 따라 동적으로 설정 + revisedBox.snp.makeConstraints { + $0.width.greaterThanOrEqualTo(60) // 최소 너비 + $0.width.lessThanOrEqualTo(120) // 최대 너비 + } + + containerView.snp.makeConstraints { + $0.height.equalTo(32) + } + + return containerView + } + +} diff --git a/Projects/CommonUI/Sources/View/Diary/DiaryTodayView.swift b/Projects/CommonUI/Sources/View/Diary/DiaryTodayView.swift new file mode 100644 index 0000000..4efb4e3 --- /dev/null +++ b/Projects/CommonUI/Sources/View/Diary/DiaryTodayView.swift @@ -0,0 +1,310 @@ +// +// DiaryTodayView.swift +// CommonUI +// +// Created by 박지윤 on 9/14/25. +// + +import UIKit +import SnapKit +import Then +import Domain + +open class DiaryTodayView: UIView { + // MARK: UI Components + private(set) var dateLabel = UILabel().then { + $0.textColor = CommonUIAssets.LMGray1 + $0.textAlignment = .center + $0.numberOfLines = 1 + $0.font = UIFont.systemFont(ofSize: 17, weight: .semibold) + } + + private let dividerView = UIView().then { + $0.backgroundColor = CommonUIAssets.LMGray5 + } + + private(set) var emotionLabel = UILabel().then { + $0.font = UIFont.systemFont(ofSize: 25) + } + + private let contentView = UIView() + + private(set) var contentLabel = UILabel().then { + $0.lineBreakMode = .byWordWrapping + $0.font = UIFont.systemFont(ofSize: 16, weight: .regular) + $0.textColor = CommonUIAssets.LMGray1 + $0.numberOfLines = 2 + + // 줄간격 설정 + let paragraphStyle = NSMutableParagraphStyle() + paragraphStyle.lineSpacing = 4 + $0.attributedText = NSAttributedString( + string: $0.text ?? "", + attributes: [.paragraphStyle: paragraphStyle] + ) + } + + private(set) var diaryAddButton = LMButton(textColor: CommonUIAssets.LMBlack, + bgColor: CommonUIAssets.LMOrange3) + + // MARK: Properties + var tap: (() -> Void)? + public var tapDiaryAdd: (() -> Void)? + public var tapDiaryDetail: ((DiaryVO?) -> Void)? + + // 현재 일기 데이터 저장 + private var currentDiaryData: DiaryVO? + + public override init(frame: CGRect) { + super.init(frame: frame) + print("initttt") + configureSubviews() + makeConstraints() + } + + required public init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + // MARK: Configuration + func configureSubviews() { + setLayer() + setGesture() + addButtonEvent() + + diaryAddButton = diaryAddButton.then { + $0.setTitle("+ 일기 추가하기", for: .normal) + } + + addSubview(dateLabel) + addSubview(dividerView) + } + + // MARK: - Public Methods + + /// 일기 데이터 설정 (String 날짜) + public func setDiaryTodayData(date: String, diaryId: Int, score: Int, content: String) { + setNonTodayView() + setDateLabel(from: date) + setEmotionAndContent(score: score, content: content) + } + + /// 일기 데이터 설정 (Date 날짜) + public func setDiaryTodayData(date: Date, diaryId: Int, score: Int, content: String) { + setNonTodayView() + setDateLabel(from: date) + setEmotionAndContent(score: score, content: content) + } + + /// DiaryVO 데이터 설정 + public func setDiaryData(_ diaryData: DiaryVO?) { + currentDiaryData = diaryData + } + + /// 빈 일기 데이터 설정 (String 날짜) + public func setDiaryTodayEmptyData(date: String) { + currentDiaryData = nil // 일기 데이터 없음 + + let formatter = DateFormatter() + formatter.dateFormat = "yyyy-MM-dd" + + if let dateObj = formatter.date(from: date) { + setDiaryTodayEmptyData(date: dateObj) + } else { + setEmptyDataWithString(date: date) + } + } + + /// 빈 일기 데이터 설정 (Date 날짜) + public func setDiaryTodayEmptyData(date: Date) { + currentDiaryData = nil // 일기 데이터 없음 + + if Calendar.current.isDateInToday(date) { + setTodayEmptyData(date: date) + } else { + setOtherDayEmptyData(date: date) + } + } + + // MARK: - Private Methods + + /// 오늘 일기 없을 때 - 일기 추가하기 버튼 표시 + private func setTodayEmptyData(date: Date) { + setupEmptyState() + setDateLabel(from: date) + setupAddButton() + } + + /// 다른 날 일기 없을 때 - 작성된 일기가 없습니다 표시 + private func setOtherDayEmptyData(date: Date) { + setNonTodayView() + setDateLabel(from: date) + setEmptyEmotionAndContent() + } + + /// String 날짜로 빈 데이터 설정 (fallback) + private func setEmptyDataWithString(date: String) { + setupEmptyState() + dateLabel.text = date.convertDateString(fromFormat: "yyyy-MM-dd", toFormat: "yyyy년 MM월 dd일") + setupAddButton() + } + + /// 빈 상태 UI 설정 + private func setupEmptyState() { + addSubview(diaryAddButton) + emotionLabel.removeFromSuperview() + contentLabel.removeFromSuperview() + } + + /// 일기 추가 버튼 설정 + private func setupAddButton() { + diaryAddButton.snp.makeConstraints { + $0.centerX.equalToSuperview() + $0.bottom.equalToSuperview().inset(15) + $0.width.equalToSuperview().inset(20) + } + diaryAddButton.addTarget(self, action: #selector(handleDiaryAddButton), for: .touchUpInside) + } + + /// 날짜 라벨 설정 (String) + private func setDateLabel(from date: String) { + if date.contains("년") && date.contains("월") && date.contains("일") { + dateLabel.text = date + } else { + dateLabel.text = date.convertDateString(fromFormat: "yyyy-MM-dd", toFormat: "yyyy년 MM월 dd일") + } + } + + /// 날짜 라벨 설정 (Date) + private func setDateLabel(from date: Date) { + let formatter = DateFormatter() + formatter.dateFormat = "yyyy년 MM월 dd일" + dateLabel.text = formatter.string(from: date) + } + + /// 이모지와 내용 설정 + private func setEmotionAndContent(score: Int, content: String) { + emotionLabel.text = getEmotionFromScore(score) + setContentWithLineSpacing(content) + } + + /// 줄간격이 적용된 텍스트 설정 + private func setContentWithLineSpacing(_ text: String) { + let paragraphStyle = NSMutableParagraphStyle() + paragraphStyle.lineSpacing = 4 + + contentLabel.attributedText = NSAttributedString( + string: text, + attributes: [ + .font: UIFont.systemFont(ofSize: 16, weight: .regular), + .foregroundColor: CommonUIAssets.LMGray1 ?? .black, + .paragraphStyle: paragraphStyle + ] + ) + } + + /// 빈 상태 이모지와 내용 설정 + private func setEmptyEmotionAndContent() { + emotionLabel.text = "🫥" + contentLabel.text = "작성된 일기가 없습니다" + } + + /// 일기가 있는 상태의 UI 설정 + private func setNonTodayView() { + diaryAddButton.removeFromSuperview() + + addSubview(contentView) + [emotionLabel, contentLabel].forEach { contentView.addSubview($0) } + + setupNonTodayConstraints() + } + + /// 일기가 있는 상태의 제약조건 설정 + private func setupNonTodayConstraints() { + contentView.snp.makeConstraints { + $0.top.equalTo(dividerView.snp.bottom).offset(15) + $0.centerX.width.equalToSuperview().inset(20) + $0.height.equalTo(55) + $0.bottom.equalToSuperview().inset(15) + } + + emotionLabel.snp.makeConstraints { + $0.leading.equalToSuperview().inset(5) + $0.centerY.equalToSuperview() + $0.width.equalTo(30) + } + + contentLabel.snp.makeConstraints { + $0.centerY.equalToSuperview() + $0.leading.equalTo(emotionLabel.snp.trailing).offset(10) + $0.trailing.equalToSuperview().inset(15) + } + } + + /// 점수에 따른 이모지 반환 + private func getEmotionFromScore(_ score: Int) -> String { + switch score { + case 90...100: return "😊" // 매우 좋음 + case 80..<90: return "🙂" // 좋음 + case 70..<80: return "😐" // 보통 + case 60..<70: return "😕" // 아쉬움 + default: return "😢" // 슬픔 + } + } + + // MARK: - Layout & Styling + + /// 뷰 레이어 스타일 설정 + private func setLayer() { + backgroundColor = .white + layer.cornerRadius = 12 + layer.borderWidth = 1 + layer.borderColor = CommonUIAssets.LMOrange1?.cgColor + layer.shadowColor = UIColor.black.cgColor + layer.shadowOpacity = 0.07 + layer.shadowOffset = CGSize(width: 0, height: 0) + layer.shadowRadius = 12 + } + + /// 제스처 설정 + private func setGesture() { + let tapGesture = UITapGestureRecognizer(target: self, action: #selector(handleTap)) + addGestureRecognizer(tapGesture) + } + + func makeConstraints() { + dateLabel.snp.makeConstraints { + $0.top.equalToSuperview().inset(20) + $0.leading.equalToSuperview().inset(20) + } + + dividerView.snp.makeConstraints { + $0.top.equalTo(dateLabel.snp.bottom).offset(10) + $0.height.equalTo(1) + $0.centerX.equalToSuperview() + $0.width.equalToSuperview().inset(20) + } + } + + // MARK: - Event Handlers + + /// 버튼 이벤트 설정 + private func addButtonEvent() { + diaryAddButton.addTarget(self, action: #selector(handleDiaryAddButton), for: .touchUpInside) + } + + /// 탭 제스처 핸들러 + @objc private func handleTap() { + // 일기 데이터가 있으면 상세 화면으로, 없으면 기본 탭 이벤트 + if let diaryData = currentDiaryData { + tapDiaryDetail?(diaryData) + } else { + tap?() + } + } + + /// 일기 추가 버튼 핸들러 + @objc private func handleDiaryAddButton() { + tapDiaryAdd?() + } +} diff --git a/Projects/CommonUI/Sources/View/Diary/DiaryView.swift b/Projects/CommonUI/Sources/View/Diary/DiaryView.swift new file mode 100644 index 0000000..d89923d --- /dev/null +++ b/Projects/CommonUI/Sources/View/Diary/DiaryView.swift @@ -0,0 +1,179 @@ +// +// DiaryView.swift +// CommonUI +// +// Created by 박지윤 on 7/1/25. +// + +import UIKit +import SnapKit +import Then +import RxSwift + +open class DiaryView: UIView { + + // MARK: UI Components + private(set) var previousButton = UIButton().then { + $0.setImage(CommonUIAssets.IconPrevious, for: .normal) + } + + private(set) var monthLabel = UILabel().then { + $0.text = "" + $0.textColor = CommonUIAssets.LMGray1 + $0.font = UIFont.systemFont(ofSize: 20, weight: .semibold) + } + + private(set) var nextButton = UIButton().then { + $0.setImage(CommonUIAssets.IconNext, for: .normal) + } + + private(set) var addButton = UIButton().then { + $0.setImage(CommonUIAssets.IconAdd, for: .normal) + } + + public var calendarView = CalendarView() + public let diaryTodayView = DiaryTodayView() + + // MARK: Properties + private var currentYear: Int + private var currentMonth: Int + + public var onAddButtonTapped: (() -> Void)? + public var tapPrevious: ((Int, Int) -> Void)? + public var tapNext: ((Int, Int) -> Void)? + let disposeBag = DisposeBag() + + public override init(frame: CGRect) { + // 현재 날짜로 초기화 + let today = Date() + let calendar = Calendar.current + self.currentYear = calendar.component(.year, from: today) + self.currentMonth = calendar.component(.month, from: today) + + super.init(frame: frame) + configureSubviews() + makeConstraints() + bindEvents() + calendarView.configureSubviews() + calendarView.makeConstraints() + setupSampleEmotionData() + updateMonthButtonTitle() + } + + required public init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + private func setupSampleEmotionData() { + // 샘플 이모지 데이터 설정 (이미지와 동일하게) + let sampleEmotions: [Int: String] = [ + 1: "😢", // 우는 얼굴 + 7: "😐", // 무표정한 얼굴 + 12: "🥳", // 파티 모자 + 13: "😡" // 화난 얼굴 + ] + calendarView.setEmotionData(sampleEmotions) + } + + // MARK: Configuration + func configureSubviews() { + addButtonEvent() + + addSubview(previousButton) + addSubview(monthLabel) + addSubview(nextButton) + addSubview(addButton) + addSubview(calendarView) + addSubview(diaryTodayView) + + backgroundColor = CommonUIAssets.LMOrange4 + } + + // MARK: Layout + func makeConstraints() { + monthLabel.snp.makeConstraints { + $0.top.equalTo(safeAreaInsets).inset(13) + $0.centerX.equalToSuperview() + } + + previousButton.snp.makeConstraints { + $0.centerY.equalTo(monthLabel) + $0.trailing.equalTo(monthLabel.snp.leading).offset(-10) + $0.height.width.equalTo(monthLabel.snp.height) + } + + nextButton.snp.makeConstraints { + $0.centerY.equalTo(monthLabel) + $0.leading.equalTo(monthLabel.snp.trailing).offset(10) + $0.height.width.equalTo(monthLabel.snp.height) + } + + addButton.snp.makeConstraints { + $0.centerY.equalTo(monthLabel) + $0.trailing.equalToSuperview().inset(20) + $0.height.width.equalTo(monthLabel.snp.height) + } + + calendarView.snp.makeConstraints { + $0.top.equalTo(monthLabel.snp.bottom).offset(17) + $0.horizontalEdges.equalToSuperview().inset(20) + $0.height.equalTo(410) + } + + diaryTodayView.snp.makeConstraints { + $0.top.equalTo(calendarView.snp.bottom).offset(3) + $0.centerX.equalToSuperview() + $0.width.equalToSuperview().inset(20) + $0.height.equalTo(141) + } + } + + func bindEvents() { + addButton.rx.tap + .subscribe(onNext: { [weak self] in + self?.onAddButtonTapped?() + }) + .disposed(by: disposeBag) + } + + // MARK: Event + private func addButtonEvent() { + previousButton.addTarget(self, action: #selector(handlePreviousButton), for: .touchUpInside) + nextButton.addTarget(self, action: #selector(handleNextButton), for: .touchUpInside) + } + + @objc private func handlePreviousButton() { + if currentMonth == 1 { + currentYear -= 1 + currentMonth = 12 + } else { + currentMonth -= 1 + } + updateMonthButtonTitle() + + // 캘린더 월 업데이트 + calendarView.updateMonth(year: currentYear, month: currentMonth) + + tapPrevious?(currentYear, currentMonth) + } + + @objc private func handleNextButton() { + if currentMonth == 12 { + currentYear += 1 + currentMonth = 1 + } else { + currentMonth += 1 + } + updateMonthButtonTitle() + + // 캘린더 월 업데이트 + calendarView.updateMonth(year: currentYear, month: currentMonth) + + tapNext?(currentYear, currentMonth) + } + + private func updateMonthButtonTitle() { + let monthText = "\(currentYear)년 \(currentMonth)월" + monthLabel.text = monthText + } +} diff --git a/Projects/CommonUI/Sources/View/DiaryView.swift b/Projects/CommonUI/Sources/View/DiaryView.swift deleted file mode 100644 index 828128a..0000000 --- a/Projects/CommonUI/Sources/View/DiaryView.swift +++ /dev/null @@ -1,7 +0,0 @@ -// -// DiaryView.swift -// CommonUI -// -// Created by 박지윤 on 7/1/25. -// - diff --git a/Projects/CommonUI/Sources/View/MyPageView.swift b/Projects/CommonUI/Sources/View/MyPageView.swift index fdc92b9..fbda76e 100644 --- a/Projects/CommonUI/Sources/View/MyPageView.swift +++ b/Projects/CommonUI/Sources/View/MyPageView.swift @@ -4,3 +4,38 @@ // // Created by 박지윤 on 7/1/25. // + +import UIKit +import SnapKit +import Then + +open class MyPageView: UIView { + public let logoutButton = UIButton().then { + $0.setTitle("로그아웃", for: .normal) + $0.setTitleColor(.white, for: .normal) + $0.backgroundColor = .systemRed + $0.layer.cornerRadius = 8 + $0.titleLabel?.font = .systemFont(ofSize: 16, weight: .medium) + } + + public override init(frame: CGRect) { + super.init(frame: frame) + initUI() + } + + required public init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + private func initUI() { + backgroundColor = .systemBackground + + addSubview(logoutButton) + + logoutButton.snp.makeConstraints { + $0.center.equalToSuperview() + $0.width.equalTo(200) + $0.height.equalTo(50) + } + } +} diff --git a/Projects/Data/Data.xcodeproj/project.pbxproj b/Projects/Data/Data.xcodeproj/project.pbxproj index 048ab16..b76edae 100644 --- a/Projects/Data/Data.xcodeproj/project.pbxproj +++ b/Projects/Data/Data.xcodeproj/project.pbxproj @@ -7,18 +7,21 @@ objects = { /* Begin PBXBuildFile section */ + 0C1776E871E0CB4A493E8354 /* DefaultDTO.swift in Sources */ = {isa = PBXBuildFile; fileRef = 377C66458E6219D981E14335 /* DefaultDTO.swift */; }; 34FD760EB97BB96E9D770BF0 /* LoginRepository.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3D7624DE90CFBBEF778E120E /* LoginRepository.swift */; }; + 3A371C0557EFC12B7A68ADB2 /* ChatRepository.swift in Sources */ = {isa = PBXBuildFile; fileRef = 662B73C60D5CDD9A179AED9B /* ChatRepository.swift */; }; 43E9C2380F425520C1FA1AD2 /* CourseDTO.swift in Sources */ = {isa = PBXBuildFile; fileRef = AAAC2885ACFE998578DC25E8 /* CourseDTO.swift */; }; + 59034BF673F78A457B681967 /* SignRepository.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC5F0E176D612624E2C929B7 /* SignRepository.swift */; }; 684AAEA9796EED3F9FC592FC /* NetworkConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5D46FD93B80D052843AD5063 /* NetworkConfiguration.swift */; }; 901ACA7B98089AB702ADA830 /* Domain.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 06FAA1459D11CCE724C34195 /* Domain.framework */; }; - 950A0D702E5CCF0200C07CF2 /* SignRepository.swift in Sources */ = {isa = PBXBuildFile; fileRef = 950A0D6F2E5CCEFD00C07CF2 /* SignRepository.swift */; }; - 950A0D902E6039D600C07CF2 /* DefaultDTO.swift in Sources */ = {isa = PBXBuildFile; fileRef = 950A0D8F2E6039D300C07CF2 /* DefaultDTO.swift */; }; - 9516ED5D2E71D3FE00F548A1 /* ChatRoomDTO.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9516ED5C2E71D3FB00F548A1 /* ChatRoomDTO.swift */; }; - 9516ED612E71D5B800F548A1 /* ChatDetailDTO.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9516ED602E71D5B200F548A1 /* ChatDetailDTO.swift */; }; - 951F3F8F2E6F36450022583B /* ChatDTO.swift in Sources */ = {isa = PBXBuildFile; fileRef = 951F3F8E2E6F36440022583B /* ChatDTO.swift */; }; - 951F3F912E6F36680022583B /* ChatRepository.swift in Sources */ = {isa = PBXBuildFile; fileRef = 951F3F902E6F36640022583B /* ChatRepository.swift */; }; + 9532C7AC2E786F3F00B4BADE /* DiaryDTO.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9532C7AB2E786F3A00B4BADE /* DiaryDTO.swift */; }; + 9532C7AE2E78707D00B4BADE /* DiaryCalendarDTO.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9532C7AD2E78707600B4BADE /* DiaryCalendarDTO.swift */; }; + 956C4D8F2E77142700E32F93 /* DiaryRepository.swift in Sources */ = {isa = PBXBuildFile; fileRef = 956C4D8E2E77140A00E32F93 /* DiaryRepository.swift */; }; + 99E14C3BCF2FDBB8004B27AC /* ChatRoomDTO.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2298B5F633729122951DB918 /* ChatRoomDTO.swift */; }; A334985695DC9388841BBC43 /* QuizRepository.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3CCA951B89F2C3D20AA31F7F /* QuizRepository.swift */; }; B2F8FBFA915F696CCCA4152A /* Alamofire.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A3B0D3D8C7049B6856791C1D /* Alamofire.framework */; }; + C69F1794E1B0FB5F7B7CBDA5 /* ChatDTO.swift in Sources */ = {isa = PBXBuildFile; fileRef = C53F21C1CF4EA22470F573D1 /* ChatDTO.swift */; }; + C9C427C4DD6EB1B302F5F219 /* ChatDetailDTO.swift in Sources */ = {isa = PBXBuildFile; fileRef = 044D04653F84020B268C53D3 /* ChatDetailDTO.swift */; }; E1BFC73FB539432F6E12CD94 /* CourseRepository.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0730BC3657E24BCEA511A3C /* CourseRepository.swift */; }; E6785667C8E247C344474CBB /* TokenRepository.swift in Sources */ = {isa = PBXBuildFile; fileRef = 050263DDA587409393A9328A /* TokenRepository.swift */; }; E9463A3FF42D5F0960245F80 /* QuizDTO.swift in Sources */ = {isa = PBXBuildFile; fileRef = 77810122262C6CB16D4D47DA /* QuizDTO.swift */; }; @@ -40,23 +43,26 @@ /* Begin PBXFileReference section */ 033B57EACF093CA22F91EAB9 /* Data-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = "Data-Info.plist"; sourceTree = ""; }; + 044D04653F84020B268C53D3 /* ChatDetailDTO.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatDetailDTO.swift; sourceTree = ""; }; 050263DDA587409393A9328A /* TokenRepository.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TokenRepository.swift; sourceTree = ""; }; 06FAA1459D11CCE724C34195 /* Domain.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Domain.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 2298B5F633729122951DB918 /* ChatRoomDTO.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatRoomDTO.swift; sourceTree = ""; }; + 377C66458E6219D981E14335 /* DefaultDTO.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DefaultDTO.swift; sourceTree = ""; }; 3CCA951B89F2C3D20AA31F7F /* QuizRepository.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QuizRepository.swift; sourceTree = ""; }; 3D7624DE90CFBBEF778E120E /* LoginRepository.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoginRepository.swift; sourceTree = ""; }; 4E75197C294DE74F5162FAA7 /* Data.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Data.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 5D46FD93B80D052843AD5063 /* NetworkConfiguration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkConfiguration.swift; sourceTree = ""; }; + 662B73C60D5CDD9A179AED9B /* ChatRepository.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatRepository.swift; sourceTree = ""; }; 77810122262C6CB16D4D47DA /* QuizDTO.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QuizDTO.swift; sourceTree = ""; }; - 950A0D6F2E5CCEFD00C07CF2 /* SignRepository.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SignRepository.swift; sourceTree = ""; }; - 950A0D8F2E6039D300C07CF2 /* DefaultDTO.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DefaultDTO.swift; sourceTree = ""; }; - 9516ED5C2E71D3FB00F548A1 /* ChatRoomDTO.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatRoomDTO.swift; sourceTree = ""; }; - 9516ED602E71D5B200F548A1 /* ChatDetailDTO.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatDetailDTO.swift; sourceTree = ""; }; - 951F3F8E2E6F36440022583B /* ChatDTO.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatDTO.swift; sourceTree = ""; }; - 951F3F902E6F36640022583B /* ChatRepository.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatRepository.swift; sourceTree = ""; }; + 9532C7AB2E786F3A00B4BADE /* DiaryDTO.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DiaryDTO.swift; sourceTree = ""; }; + 9532C7AD2E78707600B4BADE /* DiaryCalendarDTO.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DiaryCalendarDTO.swift; sourceTree = ""; }; + 956C4D8E2E77140A00E32F93 /* DiaryRepository.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DiaryRepository.swift; sourceTree = ""; }; A3B0D3D8C7049B6856791C1D /* Alamofire.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Alamofire.framework; sourceTree = BUILT_PRODUCTS_DIR; }; A44BC1E2E75FC256F832CA38 /* LoginDTO.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoginDTO.swift; sourceTree = ""; }; AAAC2885ACFE998578DC25E8 /* CourseDTO.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CourseDTO.swift; sourceTree = ""; }; B0730BC3657E24BCEA511A3C /* CourseRepository.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CourseRepository.swift; sourceTree = ""; }; + C53F21C1CF4EA22470F573D1 /* ChatDTO.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatDTO.swift; sourceTree = ""; }; + DC5F0E176D612624E2C929B7 /* SignRepository.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SignRepository.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -82,12 +88,23 @@ name = Products; sourceTree = ""; }; + 508DB87A8CFE03F2164A0BF9 /* Chat */ = { + isa = PBXGroup; + children = ( + 044D04653F84020B268C53D3 /* ChatDetailDTO.swift */, + C53F21C1CF4EA22470F573D1 /* ChatDTO.swift */, + 2298B5F633729122951DB918 /* ChatRoomDTO.swift */, + ); + path = Chat; + sourceTree = ""; + }; 647255CD65221C9CD4A43DED /* DTO */ = { isa = PBXGroup; children = ( - 9516ED642E75609400F548A1 /* Chat */, - 950A0D8F2E6039D300C07CF2 /* DefaultDTO.swift */, + 9532C7AA2E786F0000B4BADE /* Diary */, + 508DB87A8CFE03F2164A0BF9 /* Chat */, AAAC2885ACFE998578DC25E8 /* CourseDTO.swift */, + 377C66458E6219D981E14335 /* DefaultDTO.swift */, A44BC1E2E75FC256F832CA38 /* LoginDTO.swift */, 77810122262C6CB16D4D47DA /* QuizDTO.swift */, ); @@ -97,12 +114,13 @@ 73F3ED55BFDC2EF15878F5B6 /* Repository */ = { isa = PBXGroup; children = ( - 951F3F902E6F36640022583B /* ChatRepository.swift */, - 950A0D6F2E5CCEFD00C07CF2 /* SignRepository.swift */, - B0730BC3657E24BCEA511A3C /* CourseRepository.swift */, + DC5F0E176D612624E2C929B7 /* SignRepository.swift */, 3D7624DE90CFBBEF778E120E /* LoginRepository.swift */, - 3CCA951B89F2C3D20AA31F7F /* QuizRepository.swift */, 050263DDA587409393A9328A /* TokenRepository.swift */, + 3CCA951B89F2C3D20AA31F7F /* QuizRepository.swift */, + B0730BC3657E24BCEA511A3C /* CourseRepository.swift */, + 662B73C60D5CDD9A179AED9B /* ChatRepository.swift */, + 956C4D8E2E77140A00E32F93 /* DiaryRepository.swift */, ); path = Repository; sourceTree = ""; @@ -115,14 +133,13 @@ ); sourceTree = ""; }; - 9516ED642E75609400F548A1 /* Chat */ = { + 9532C7AA2E786F0000B4BADE /* Diary */ = { isa = PBXGroup; children = ( - 951F3F8E2E6F36440022583B /* ChatDTO.swift */, - 9516ED5C2E71D3FB00F548A1 /* ChatRoomDTO.swift */, - 9516ED602E71D5B200F548A1 /* ChatDetailDTO.swift */, + 9532C7AB2E786F3A00B4BADE /* DiaryDTO.swift */, + 9532C7AD2E78707600B4BADE /* DiaryCalendarDTO.swift */, ); - path = Chat; + path = Diary; sourceTree = ""; }; A46DA33BCE1E2288153B2AC3 /* Network */ = { @@ -232,20 +249,23 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 9516ED612E71D5B800F548A1 /* ChatDetailDTO.swift in Sources */, + C69F1794E1B0FB5F7B7CBDA5 /* ChatDTO.swift in Sources */, + C9C427C4DD6EB1B302F5F219 /* ChatDetailDTO.swift in Sources */, + 99E14C3BCF2FDBB8004B27AC /* ChatRoomDTO.swift in Sources */, 43E9C2380F425520C1FA1AD2 /* CourseDTO.swift in Sources */, - 9516ED5D2E71D3FE00F548A1 /* ChatRoomDTO.swift in Sources */, + 0C1776E871E0CB4A493E8354 /* DefaultDTO.swift in Sources */, FF43B3A4D0DC88307E918DB0 /* LoginDTO.swift in Sources */, E9463A3FF42D5F0960245F80 /* QuizDTO.swift in Sources */, 684AAEA9796EED3F9FC592FC /* NetworkConfiguration.swift in Sources */, - 950A0D702E5CCF0200C07CF2 /* SignRepository.swift in Sources */, - 951F3F8F2E6F36450022583B /* ChatDTO.swift in Sources */, + 3A371C0557EFC12B7A68ADB2 /* ChatRepository.swift in Sources */, + 9532C7AC2E786F3F00B4BADE /* DiaryDTO.swift in Sources */, E1BFC73FB539432F6E12CD94 /* CourseRepository.swift in Sources */, - 951F3F912E6F36680022583B /* ChatRepository.swift in Sources */, 34FD760EB97BB96E9D770BF0 /* LoginRepository.swift in Sources */, - 950A0D902E6039D600C07CF2 /* DefaultDTO.swift in Sources */, A334985695DC9388841BBC43 /* QuizRepository.swift in Sources */, + 59034BF673F78A457B681967 /* SignRepository.swift in Sources */, E6785667C8E247C344474CBB /* TokenRepository.swift in Sources */, + 9532C7AE2E78707D00B4BADE /* DiaryCalendarDTO.swift in Sources */, + 956C4D8F2E77142700E32F93 /* DiaryRepository.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -309,7 +329,7 @@ SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; SWIFT_COMPILATION_MODE = wholemodule; - SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; + SWIFT_OPTIMIZATION_LEVEL = "-O"; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; VERSIONING_SYSTEM = "apple-generic"; diff --git a/Projects/Data/Sources/DTO/Diary/DiaryCalendarDTO.swift b/Projects/Data/Sources/DTO/Diary/DiaryCalendarDTO.swift new file mode 100644 index 0000000..31bdbfc --- /dev/null +++ b/Projects/Data/Sources/DTO/Diary/DiaryCalendarDTO.swift @@ -0,0 +1,40 @@ +// +// DiaryCalendarDTO.swift +// Data +// +// Created by 박지윤 on 9/16/25. +// + +import Domain + +public struct DiaryCalendarResponseDTO: Decodable { + public let is_success: Bool + public let code: String + public let message: String + public let data: DiaryCalendarDataDTO +} + +public struct DiaryCalendarDataDTO: Decodable { + public let year: Int + public let month: Int + public let diaryList: [DiaryListDTO] +} +public struct DiaryListDTO: Decodable { + public let diaryId: Int + public let createdAt: String + public let score: Int +} + +extension DiaryCalendarDataDTO { + func toDomain() -> DiaryCalendarVO { + let diaryVOList = diaryList.map { diary in + DiaryListVO(diaryId: diary.diaryId, + createdAt: diary.createdAt, + score: diary.score) + } + + return DiaryCalendarVO(year: year, + month: month, + diaryList: diaryVOList) + } +} diff --git a/Projects/Data/Sources/DTO/Diary/DiaryDTO.swift b/Projects/Data/Sources/DTO/Diary/DiaryDTO.swift new file mode 100644 index 0000000..49a67ae --- /dev/null +++ b/Projects/Data/Sources/DTO/Diary/DiaryDTO.swift @@ -0,0 +1,65 @@ +// +// DiaryDTO.swift +// Data +// +// Created by 박지윤 on 9/16/25. +// + +import Domain + +public struct DiaryResponseDTO: Decodable { + public let is_success: Bool + public let code: String + public let message: String + public let data: DiaryDataDTO +} + +public struct DiaryDataDTO: Decodable { + public let diaryId: Int + public let createdAt: String + public let originContent: String + public let spellingDto: SpellingDTO + public let feedback: String +} + +extension DiaryDataDTO { + func toDomain() -> DiaryVO { + let revisionVO = spellingDto.revisions.map { revision in + RevisionVO( + originContent: revision.originContent, + revisedContent: revision.revisedContent, + beginOffset: revision.beginOffset, + comment: revision.comment, + category: revision.category, + examples: revision.examples + ) + } + + let spellingVO = SpellingVO( + revisedContent: spellingDto.revisedContent, + score: spellingDto.score, + revisions: revisionVO) + + return DiaryVO( + diaryId: diaryId, + createdAt: createdAt, + originContent: originContent, + spellingDto: spellingVO, + feedback: feedback) + } +} + +public struct SpellingDTO: Decodable { + public let revisedContent: String + public let score: Int + public let revisions: [RevisionDTO] +} + +public struct RevisionDTO: Decodable { + public let originContent: String + public let revisedContent: String + public let beginOffset: Int + public let comment: String + public let category: String + public let examples: [String] +} diff --git a/Projects/Data/Sources/Repository/DiaryRepository.swift b/Projects/Data/Sources/Repository/DiaryRepository.swift new file mode 100644 index 0000000..a0a9963 --- /dev/null +++ b/Projects/Data/Sources/Repository/DiaryRepository.swift @@ -0,0 +1,136 @@ +// +// DiaryRepository.swift +// Data +// +// Created by 박지윤 on 9/15/25. +// + +import Domain +import RxSwift +import Alamofire +import Foundation + +public class DefaultDiaryRepository: DiaryRepository { + private let tokenRepository: TokenRepository + + public init(tokenRepository: TokenRepository) { + self.tokenRepository = tokenRepository + } + + public func postDiary(content: String) -> Single { + let parameter = ["content": content] + + return request(method: .post, + parameters: parameter, + endpoint: "/api/diaries", + responseType: DiaryResponseDTO.self + ) + .map { dto in + return dto.data.toDomain() + } + } + + public func getDiary(date: Date) -> Single { + let formatter = DateFormatter() + formatter.dateFormat = "yyyy-MM-dd" + let dateString = formatter.string(from: date) + let parameter = ["date": dateString] + + return request(parameters: parameter, + endpoint: "/api/diaries", + encoding: URLEncoding.default, + responseType: DiaryResponseDTO.self + ) + .map { dto in + return dto.data.toDomain() + } + } + + public func getDiaryDetail(diaryId: Int, date: Date) -> Single { + let formatter = DateFormatter() + formatter.dateFormat = "yyyy-MM-dd" + let dateString = formatter.string(from: date) + let parameter = ["date": dateString] + + return request(parameters: parameter, + endpoint: "/api/diaries/\(diaryId)", + encoding: URLEncoding.default, + responseType: DiaryResponseDTO.self + ) + .map { dto in + return dto.data.toDomain() + } + } + + public func deleteDiaryDetail(diaryId: Int) -> Single { + return request(method: .delete, + endpoint: "/api/diaries/\(diaryId)", + responseType: DefaultDTO.self + ) + .map { dto in + return dto.getMessage() + } + } + + public func getDiaryCalendar(year: Int, month: Int) -> Single { + let parameter = ["year": year, + "month": month] + + return request(parameters: parameter, + endpoint: "/api/diaries/calendar", + encoding: URLEncoding.default, + responseType: DiaryCalendarResponseDTO.self + ) + .map { dto in + return dto.data.toDomain() + } + } + + private func request( + method: HTTPMethod = .get, + parameters: [String: Any]? = nil, + endpoint: String, + encoding: ParameterEncoding = JSONEncoding.default, + responseType: T.Type + ) -> Single { + return Single.create { single in + let url = "\(NetworkConfiguration.baseUrl)\(endpoint)" + var headers: HTTPHeaders = [:] + + if let token = self.tokenRepository.getAccessToken() { + print("🔑 사용할 토큰: \(token)") + headers.add(name: "Authorization", value: "Bearer \(token)") + } else { + print("❌ 토큰이 없습니다!") + } + print("🌐 API 요청 URL: \(url)") + print("🔑 Authorization 헤더: \(headers)") + print("📤 요청 파라미터: \(parameters ?? [:])") + print("📤 요청 메서드: \(method)") + print("📤 인코딩: \(encoding)") + + let request = AF.request(url, + method: method, + parameters: parameters, + encoding: encoding, + headers: headers) + .responseDecodable(of: responseType) { response in + print("📊 HTTP 상태 코드: \(response.response?.statusCode ?? -1)") + if let data = response.data { + print("📊 응답 데이터: \(String(data: data, encoding: .utf8) ?? "데이터 파싱 실패")") + } + + switch response.result { + case .success(let value): + print("✅ API 응답 성공: \(value)") + single(.success(value)) + case .failure(let error): + print("❌ API 응답 실패: \(error)") + single(.failure(error)) + } + } + + return Disposables.create { request.cancel() } + } + } +} diff --git a/Projects/Data/Sources/Repository/TokenRepository.swift b/Projects/Data/Sources/Repository/TokenRepository.swift index c3fc51a..ce339c6 100644 --- a/Projects/Data/Sources/Repository/TokenRepository.swift +++ b/Projects/Data/Sources/Repository/TokenRepository.swift @@ -7,6 +7,8 @@ import Domain import Foundation +import RxSwift +import Alamofire public class DefaultTokenRepository: TokenRepository { private let userDefaults = UserDefaults.standard @@ -44,4 +46,41 @@ public class DefaultTokenRepository: TokenRepository { userDefaults.removeObject(forKey: refreshToken) print("🗑️ Refresh Token cleared") } + + public func validateToken() -> Single { + guard let accessToken = getAccessToken(), !accessToken.isEmpty else { + print("❌ 토큰이 없습니다") + return Single.just(false) + } + + // 기존 API를 사용해서 토큰 유효성 검증 (courses API 호출) + return Single.create { single in + let url = "\(NetworkConfiguration.baseUrl)/api/courses" + let headers: HTTPHeaders = [ + "Authorization": "Bearer \(accessToken)" + ] + + print("🔍 토큰 검증 요청: \(url)") + + let request = AF.request(url, + method: .get, + headers: headers) + .validate() + .response { response in + switch response.result { + case .success: + print("✅ 토큰 검증 성공") + single(.success(true)) + case .failure(let error): + print("❌ 토큰 검증 실패: \(error)") + // 토큰이 무효하면 저장된 토큰 삭제 + self.clearAccessToken() + self.clearRefreshToken() + single(.success(false)) + } + } + + return Disposables.create { request.cancel() } + } + } } diff --git a/Projects/Diary/Diary.xcodeproj/project.pbxproj b/Projects/Diary/Diary.xcodeproj/project.pbxproj index 3eba568..e1ab0b9 100644 --- a/Projects/Diary/Diary.xcodeproj/project.pbxproj +++ b/Projects/Diary/Diary.xcodeproj/project.pbxproj @@ -3,7 +3,7 @@ archiveVersion = 1; classes = { }; - objectVersion = 55; + objectVersion = 56; objects = { /* Begin PBXBuildFile section */ @@ -13,6 +13,9 @@ 885CDEFC2FEB10BFC254554E /* DiaryCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 34C8A4CB2B8E262187DFF900 /* DiaryCoordinator.swift */; }; 891164E05D3D4DA36A21B781 /* DiaryViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C343285D2570E6982BB4E0F2 /* DiaryViewController.swift */; }; 94B961B5E2FDEBC84B982CCF /* Diary.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8C2AAFED786D950F229E6BA2 /* Diary.framework */; }; + 9532C7B62E79C66E00B4BADE /* DiaryResultViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9532C7B52E79C66800B4BADE /* DiaryResultViewController.swift */; }; + 95C68FAD2E7867F90017D28B /* DiaryAddViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 95C68FAC2E7867F30017D28B /* DiaryAddViewController.swift */; }; + 95EDE8D32E7C55B00091ED75 /* DiaryDetailViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 95EDE8D22E7C55AA0091ED75 /* DiaryDetailViewController.swift */; }; D2B984C26D53890B6DBB6050 /* CommonUI.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2BEC8F153FC27AECC51B0518 /* CommonUI.framework */; }; D7AE472CEBD34DE939469DBE /* DiaryViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = C13DC7064656FEAE0C95748F /* DiaryViewModel.swift */; }; D8F7B770E4A729F83E48FA58 /* DiaryTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C2C1E8068669524427E86AA /* DiaryTests.swift */; }; @@ -64,6 +67,9 @@ 474CB151776A3DCDA73ECBCF /* DiaryTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = DiaryTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 4DB32AAE1BAD4767CE6D9F22 /* Then.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Then.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 8C2AAFED786D950F229E6BA2 /* Diary.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Diary.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 9532C7B52E79C66800B4BADE /* DiaryResultViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DiaryResultViewController.swift; sourceTree = ""; }; + 95C68FAC2E7867F30017D28B /* DiaryAddViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DiaryAddViewController.swift; sourceTree = ""; }; + 95EDE8D22E7C55AA0091ED75 /* DiaryDetailViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DiaryDetailViewController.swift; sourceTree = ""; }; C13DC7064656FEAE0C95748F /* DiaryViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DiaryViewModel.swift; sourceTree = ""; }; C343285D2570E6982BB4E0F2 /* DiaryViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DiaryViewController.swift; sourceTree = ""; }; E57A12A246065D220B5FF7D4 /* Diary-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = "Diary-Info.plist"; sourceTree = ""; }; @@ -150,7 +156,10 @@ A3E9C9C2982E99C8AD377407 /* View */ = { isa = PBXGroup; children = ( + 95EDE8D22E7C55AA0091ED75 /* DiaryDetailViewController.swift */, C343285D2570E6982BB4E0F2 /* DiaryViewController.swift */, + 95C68FAC2E7867F30017D28B /* DiaryAddViewController.swift */, + 9532C7B52E79C66800B4BADE /* DiaryResultViewController.swift */, ); path = View; sourceTree = ""; @@ -238,8 +247,6 @@ isa = PBXProject; attributes = { BuildIndependentTargetsInParallel = YES; - TargetAttributes = { - }; }; buildConfigurationList = 21D1B3D84AAE7B6E00AA6ED5 /* Build configuration list for PBXProject "Diary" */; compatibilityVersion = "Xcode 14.0"; @@ -290,8 +297,11 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 9532C7B62E79C66E00B4BADE /* DiaryResultViewController.swift in Sources */, + 95EDE8D32E7C55B00091ED75 /* DiaryDetailViewController.swift in Sources */, 885CDEFC2FEB10BFC254554E /* DiaryCoordinator.swift in Sources */, 891164E05D3D4DA36A21B781 /* DiaryViewController.swift in Sources */, + 95C68FAD2E7867F90017D28B /* DiaryAddViewController.swift in Sources */, D7AE472CEBD34DE939469DBE /* DiaryViewModel.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -314,6 +324,7 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; HEADER_SEARCH_PATHS = ( "$(inherited)", + "$(SRCROOT)/../../Tuist/.build/checkouts/FSCalendar/FSCalendar/include", "$(SRCROOT)/../../Tuist/.build/checkouts/RxSwift/Sources/RxCocoaRuntime/include", ); INFOPLIST_FILE = "Derived/InfoPlists/DiaryTests-Info.plist"; @@ -324,13 +335,10 @@ ); OTHER_CFLAGS = ( "$(inherited)", + "-fmodule-map-file=$(SRCROOT)/../../Tuist/.build/checkouts/FSCalendar/FSCalendar/include/module.modulemap", "-fmodule-map-file=$(SRCROOT)/../../Tuist/.build/tuist-derived/RxCocoaRuntime/RxCocoaRuntime.modulemap", ); - OTHER_SWIFT_FLAGS = ( - "$(inherited)", - "-Xcc", - "-fmodule-map-file=$(SRCROOT)/../../Tuist/.build/tuist-derived/RxCocoaRuntime/RxCocoaRuntime.modulemap", - ); + OTHER_SWIFT_FLAGS = "$(inherited) -Xcc -fmodule-map-file=$(SRCROOT)/../../Tuist/.build/checkouts/FSCalendar/FSCalendar/include/module.modulemap -Xcc -fmodule-map-file=$(SRCROOT)/../../Tuist/.build/tuist-derived/RxCocoaRuntime/RxCocoaRuntime.modulemap"; PRODUCT_BUNDLE_IDENTIFIER = io.tuist.DiaryTests; PRODUCT_NAME = DiaryTests; SDKROOT = iphoneos; @@ -338,7 +346,7 @@ SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; SWIFT_COMPILATION_MODE = wholemodule; - SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; + SWIFT_OPTIMIZATION_LEVEL = "-O"; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; }; @@ -355,6 +363,7 @@ DYLIB_INSTALL_NAME_BASE = "@rpath"; HEADER_SEARCH_PATHS = ( "$(inherited)", + "$(SRCROOT)/../../Tuist/.build/checkouts/FSCalendar/FSCalendar/include", "$(SRCROOT)/../../Tuist/.build/checkouts/RxSwift/Sources/RxCocoaRuntime/include", ); INFOPLIST_FILE = "Derived/InfoPlists/Diary-Info.plist"; @@ -366,13 +375,10 @@ ); OTHER_CFLAGS = ( "$(inherited)", + "-fmodule-map-file=$(SRCROOT)/../../Tuist/.build/checkouts/FSCalendar/FSCalendar/include/module.modulemap", "-fmodule-map-file=$(SRCROOT)/../../Tuist/.build/tuist-derived/RxCocoaRuntime/RxCocoaRuntime.modulemap", ); - OTHER_SWIFT_FLAGS = ( - "$(inherited)", - "-Xcc", - "-fmodule-map-file=$(SRCROOT)/../../Tuist/.build/tuist-derived/RxCocoaRuntime/RxCocoaRuntime.modulemap", - ); + OTHER_SWIFT_FLAGS = "$(inherited) -Xcc -fmodule-map-file=$(SRCROOT)/../../Tuist/.build/checkouts/FSCalendar/FSCalendar/include/module.modulemap -Xcc -fmodule-map-file=$(SRCROOT)/../../Tuist/.build/tuist-derived/RxCocoaRuntime/RxCocoaRuntime.modulemap"; PRODUCT_BUNDLE_IDENTIFIER = io.tuist.Diary; PRODUCT_NAME = Diary; SDKROOT = iphoneos; @@ -380,10 +386,7 @@ SUPPORTS_MACCATALYST = NO; SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; - SWIFT_ACTIVE_COMPILATION_CONDITIONS = ( - "$(inherited)", - DEBUG, - ); + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) DEBUG"; SWIFT_COMPILATION_MODE = singlefile; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 5.0; @@ -399,6 +402,7 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; HEADER_SEARCH_PATHS = ( "$(inherited)", + "$(SRCROOT)/../../Tuist/.build/checkouts/FSCalendar/FSCalendar/include", "$(SRCROOT)/../../Tuist/.build/checkouts/RxSwift/Sources/RxCocoaRuntime/include", ); INFOPLIST_FILE = "Derived/InfoPlists/DiaryTests-Info.plist"; @@ -409,23 +413,17 @@ ); OTHER_CFLAGS = ( "$(inherited)", + "-fmodule-map-file=$(SRCROOT)/../../Tuist/.build/checkouts/FSCalendar/FSCalendar/include/module.modulemap", "-fmodule-map-file=$(SRCROOT)/../../Tuist/.build/tuist-derived/RxCocoaRuntime/RxCocoaRuntime.modulemap", ); - OTHER_SWIFT_FLAGS = ( - "$(inherited)", - "-Xcc", - "-fmodule-map-file=$(SRCROOT)/../../Tuist/.build/tuist-derived/RxCocoaRuntime/RxCocoaRuntime.modulemap", - ); + OTHER_SWIFT_FLAGS = "$(inherited) -Xcc -fmodule-map-file=$(SRCROOT)/../../Tuist/.build/checkouts/FSCalendar/FSCalendar/include/module.modulemap -Xcc -fmodule-map-file=$(SRCROOT)/../../Tuist/.build/tuist-derived/RxCocoaRuntime/RxCocoaRuntime.modulemap"; PRODUCT_BUNDLE_IDENTIFIER = io.tuist.DiaryTests; PRODUCT_NAME = DiaryTests; SDKROOT = iphoneos; SUPPORTS_MACCATALYST = NO; SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; - SWIFT_ACTIVE_COMPILATION_CONDITIONS = ( - "$(inherited)", - DEBUG, - ); + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) DEBUG"; SWIFT_COMPILATION_MODE = singlefile; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 5.0; @@ -444,6 +442,7 @@ DYLIB_INSTALL_NAME_BASE = "@rpath"; HEADER_SEARCH_PATHS = ( "$(inherited)", + "$(SRCROOT)/../../Tuist/.build/checkouts/FSCalendar/FSCalendar/include", "$(SRCROOT)/../../Tuist/.build/checkouts/RxSwift/Sources/RxCocoaRuntime/include", ); INFOPLIST_FILE = "Derived/InfoPlists/Diary-Info.plist"; @@ -455,13 +454,10 @@ ); OTHER_CFLAGS = ( "$(inherited)", + "-fmodule-map-file=$(SRCROOT)/../../Tuist/.build/checkouts/FSCalendar/FSCalendar/include/module.modulemap", "-fmodule-map-file=$(SRCROOT)/../../Tuist/.build/tuist-derived/RxCocoaRuntime/RxCocoaRuntime.modulemap", ); - OTHER_SWIFT_FLAGS = ( - "$(inherited)", - "-Xcc", - "-fmodule-map-file=$(SRCROOT)/../../Tuist/.build/tuist-derived/RxCocoaRuntime/RxCocoaRuntime.modulemap", - ); + OTHER_SWIFT_FLAGS = "$(inherited) -Xcc -fmodule-map-file=$(SRCROOT)/../../Tuist/.build/checkouts/FSCalendar/FSCalendar/include/module.modulemap -Xcc -fmodule-map-file=$(SRCROOT)/../../Tuist/.build/tuist-derived/RxCocoaRuntime/RxCocoaRuntime.modulemap"; PRODUCT_BUNDLE_IDENTIFIER = io.tuist.Diary; PRODUCT_NAME = Diary; SDKROOT = iphoneos; @@ -470,7 +466,7 @@ SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; SWIFT_COMPILATION_MODE = wholemodule; - SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; + SWIFT_OPTIMIZATION_LEVEL = "-O"; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; VERSIONING_SYSTEM = "apple-generic"; diff --git a/Projects/Diary/Sources/View/DiaryAddViewController.swift b/Projects/Diary/Sources/View/DiaryAddViewController.swift new file mode 100644 index 0000000..71bcc28 --- /dev/null +++ b/Projects/Diary/Sources/View/DiaryAddViewController.swift @@ -0,0 +1,169 @@ +// +// DiaryAddViewController.swift +// Diary +// +// Created by 박지윤 on 9/16/25. +// + +import UIKit +import CommonUI +import RxSwift +import Domain + +public class DiaryAddViewController: BaseViewController { + let viewModel: DiaryViewModel + let diaryAddView = DiaryAddView() + let loadingView = DiaryLoadingView() + + let navigationBar = DefaultNavigationBar(leftImage: CommonUIAssets.IconBack ?? nil, + rightImage: nil, + title: "일기 작성") + + public init(diaryViewModel: DiaryViewModel) { + self.viewModel = diaryViewModel + super.init() + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + public override func viewWillAppear(_ animated: Bool) { + super.viewWillAppear(animated) + self.navigationController?.setNavigationBarHidden(true, animated: false) + } + + public override func viewDidLoad() { + super.viewDidLoad() + view.backgroundColor = CommonUIAssets.LMWhite + navigationController?.navigationBar.isHidden = true + setupViewProperty() + setupHierarchy() + setupLayout() + bindData() + bindEvents() + } + + public override func setupViewProperty() { + } + + public override func setupHierarchy() { + [navigationBar, diaryAddView, loadingView] + .forEach { view.addSubview($0) } + } + + public override func setupDelegate() { + } + + public override func setupLayout() { + navigationBar.snp.makeConstraints { + $0.top.equalTo(view.safeAreaLayoutGuide) + $0.width.centerX.equalToSuperview() + } + + diaryAddView.snp.makeConstraints { + $0.top.equalTo(navigationBar.snp.bottom) + $0.horizontalEdges.bottom.equalToSuperview() + } + + loadingView.snp.makeConstraints { + $0.edges.equalToSuperview() + } + + navigationBar.isHidden = false + loadingView.isHidden = true + } + + private func showAnalysisLoading() { + print("🔄 일기 분석 시작") + + // 로딩 화면 표시 + loadingView.isHidden = false + loadingView.alpha = 0 + navigationBar.isHidden = true + + UIView.animate(withDuration: 0.3) { + self.loadingView.alpha = 1 + } + } + + private func hideAnalysisLoading() { + UIView.animate(withDuration: 0.3, animations: { + self.loadingView.alpha = 0 + }) { _ in + self.loadingView.isHidden = true + self.navigationBar.isHidden = false + } + } + + private func showToast(message: String) { + let toastLabel = UILabel() + toastLabel.backgroundColor = UIColor.black.withAlphaComponent(0.8) + toastLabel.textColor = UIColor.white + toastLabel.textAlignment = .center + toastLabel.font = UIFont.systemFont(ofSize: 16) + toastLabel.text = message + toastLabel.alpha = 0.0 + toastLabel.layer.cornerRadius = 8 + toastLabel.clipsToBounds = true + + view.addSubview(toastLabel) + + toastLabel.snp.makeConstraints { + $0.centerX.equalToSuperview() + $0.bottom.equalTo(view.safeAreaLayoutGuide).inset(100) + $0.width.lessThanOrEqualToSuperview().inset(40) + $0.height.greaterThanOrEqualTo(50) + } + + UIView.animate(withDuration: 0.3, animations: { + toastLabel.alpha = 1.0 + }) { _ in + UIView.animate(withDuration: 0.3, delay: 2.0, animations: { + toastLabel.alpha = 0.0 + }) { _ in + toastLabel.removeFromSuperview() + } + } + } + + private func navigateToAnalysisResult(diaryData: DiaryVO) { + print("📊 분석 결과 화면으로 이동") + + let diaryResultViewController = DiaryResultViewController(diaryViewModel: viewModel, diaryData: diaryData) + diaryResultViewController.modalPresentationStyle = .fullScreen + + present(diaryResultViewController, animated: true) + } + + private func bindData() { + viewModel.onDiaryPostSuccess = { [weak self] diaryData in + DispatchQueue.main.async { + self?.hideAnalysisLoading() + self?.navigateToAnalysisResult(diaryData: diaryData) + } + } + + viewModel.onDiaryPostFailure = { [weak self] error in + DispatchQueue.main.async { + self?.hideAnalysisLoading() + self?.showToast(message: " 일기 작성에 실패했습니다. 다시 시도해주세요. ") + } + } + } + + private func bindEvents() { + diaryAddView.onAddButtonTapped = { [weak self] content in + guard let self = self else { return } + + if content.isEmpty { + print("❌ 일기 내용이 비어있습니다") + return + } + + print("📝 일기 작성 요청: \(content)") + self.showAnalysisLoading() + self.viewModel.postDiary(content: content) + } + } +} diff --git a/Projects/Diary/Sources/View/DiaryDetailViewController.swift b/Projects/Diary/Sources/View/DiaryDetailViewController.swift new file mode 100644 index 0000000..623759b --- /dev/null +++ b/Projects/Diary/Sources/View/DiaryDetailViewController.swift @@ -0,0 +1,76 @@ +// +// DiaryDetailViewController.swift +// Diary +// +// Created by 박지윤 on 9/18/25. +// + +import UIKit +import CommonUI +import Domain + +public class DiaryDetailViewController: BaseViewController { + let diaryDetailView = DiaryDetailView() + let viewModel: DiaryViewModel + + let navigationBar = DefaultNavigationBar(leftImage: CommonUIAssets.IconBack ?? nil, + rightImage: nil, + title: "일기 상세") + + private let diaryData: DiaryVO + + public init(diaryViewModel: DiaryViewModel, diaryData: DiaryVO) { + self.viewModel = diaryViewModel + self.diaryData = diaryData + super.init() + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + public override func viewDidLoad() { + super.viewDidLoad() + setupViewProperty() + setupHierarchy() + setupLayout() + bindEvents() + configureData() + } + + public override func setupViewProperty() { + } + + public override func setupHierarchy() { + view.addSubview(navigationBar) + view.addSubview(diaryDetailView) + } + + public override func setupDelegate() { + } + + public override func setupLayout() { + navigationBar.snp.makeConstraints { + $0.top.equalTo(view.safeAreaLayoutGuide) + $0.width.centerX.equalToSuperview() + } + + diaryDetailView.snp.makeConstraints { + $0.top.equalTo(navigationBar.snp.bottom) + $0.leading.trailing.bottom.equalToSuperview() + } + } + + public override func setupBind() { + } + + @objc private func handleRightButtonTapped() { + } + + private func bindEvents() { + } + + private func configureData() { + diaryDetailView.configure(with: diaryData) + } +} diff --git a/Projects/Diary/Sources/View/DiaryResultViewController.swift b/Projects/Diary/Sources/View/DiaryResultViewController.swift new file mode 100644 index 0000000..a4496cb --- /dev/null +++ b/Projects/Diary/Sources/View/DiaryResultViewController.swift @@ -0,0 +1,119 @@ +// +// DiaryResultViewController.swift +// Diary +// +// Created by 박지윤 on 1/7/25. +// + +import UIKit +import CommonUI +import Domain + +public class DiaryResultViewController: BaseViewController { + let diaryResultView = DiaryResultView() + let viewModel: DiaryViewModel + + let navigationBar = DefaultNavigationBar(leftImage: nil, + rightImage: CommonUIAssets.IconClose ?? nil, + title: "일기 맞춤법 검사 결과") + + private let diaryData: DiaryVO + + public init(diaryViewModel: DiaryViewModel, diaryData: DiaryVO) { + self.viewModel = diaryViewModel + self.diaryData = diaryData + super.init() + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + public override func viewDidLoad() { + super.viewDidLoad() + setupViewProperty() + setupHierarchy() + setupLayout() + setupActions() + bindEvents() + configureData() + } + + public override func setupViewProperty() { + } + + public override func setupHierarchy() { + view.addSubview(navigationBar) + view.addSubview(diaryResultView) + } + + public override func setupDelegate() { + } + + public override func setupLayout() { + navigationBar.snp.makeConstraints { + $0.top.equalTo(view.safeAreaLayoutGuide) + $0.width.centerX.equalToSuperview() + } + + diaryResultView.snp.makeConstraints { + $0.top.equalTo(navigationBar.snp.bottom) + $0.leading.trailing.bottom.equalToSuperview() + } + } + + public override func setupBind() { + } + + public func setupActions() { + // 기존 타겟들을 모두 제거 + navigationBar.rightButton.removeTarget(navigationBar, action: nil, for: .touchUpInside) + // 새로운 타겟 추가 + navigationBar.rightButton.addTarget(self, action: #selector(handleRightButtonTapped), for: .touchUpInside) + } + + @objc private func handleRightButtonTapped() { + showExitConfirmationAlert() + } + + private func showExitConfirmationAlert() { + let lmAlert = LMAlert(title: "저장하지 않은 일기는 사라집니다.\n그래도 나가시겠습니까?") + + lmAlert.setCancelAction { + } + + lmAlert.setConfirmAction { [weak self] in + self?.deleteDiaryAndExit() + } + + lmAlert.show(in: view) + } + + private func deleteDiaryAndExit() { + viewModel.deleteDiaryDetail(diaryId: diaryData.diaryId) + + // 모달 닫기 + dismiss(animated: true) + } + + private func bindEvents() { + // leftButton이 nil이므로 이벤트 바인딩 제거 + // navigationBar.leftButton.rx.tap + // .subscribe(onNext: { [weak self] in + // self?.navigationController?.popViewController(animated: true) + // }) + // .disposed(by: disposeBag) + + diaryResultView.onSaveButtonTapped = { [weak self] in + self?.handleSaveDiary() + } + } + + private func handleSaveDiary() { + self.navigationController?.popViewController(animated: true) + } + + private func configureData() { + diaryResultView.configure(with: diaryData) + } +} diff --git a/Projects/Diary/Sources/View/DiaryViewController.swift b/Projects/Diary/Sources/View/DiaryViewController.swift index 0ec81f7..a81954a 100644 --- a/Projects/Diary/Sources/View/DiaryViewController.swift +++ b/Projects/Diary/Sources/View/DiaryViewController.swift @@ -4,3 +4,269 @@ // // Created by 박지윤 on 7/1/25. // + +import UIKit +import CommonUI +import RxSwift +import Domain + +public class DiaryViewController: BaseViewController { + let viewModel: DiaryViewModel + let diaryView = DiaryView() + + // 첫 진입 여부를 추적하는 플래그 + private var isFirstAppearance = true + + public init(diaryViewModel: DiaryViewModel) { + self.viewModel = diaryViewModel + super.init() + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + public override func viewWillAppear(_ animated: Bool) { + super.viewWillAppear(animated) + self.navigationController?.setNavigationBarHidden(true, animated: false) + + // 첫 진입 시에만 데이터 로드 + if isFirstAppearance { + let today = Date() + let calendar = Calendar.current + let year = calendar.component(.year, from: today) + let month = calendar.component(.month, from: today) + + // 캘린더 데이터 로드 + viewModel.getDiaryCalendar(year: year, month: month) + + // 오늘 날짜의 일기 데이터 로드 + viewModel.getDiary(date: today) + + isFirstAppearance = false + } + } + + public override func viewDidLoad() { + super.viewDidLoad() + view.backgroundColor = CommonUIAssets.LMOrange4 + navigationController?.navigationBar.isHidden = true + setupViewProperty() + setupHierarchy() + setupLayout() + bindData() + bindEvents() + + // 첫 진입 시 오늘 날짜로 초기 상태 설정 + setupInitialState() + } + + public override func setupViewProperty() { + } + + public override func setupHierarchy() { + view.addSubview(diaryView) + } + + public override func setupDelegate() { + } + + public override func setupLayout() { + diaryView.snp.makeConstraints { + $0.edges.equalTo(view.safeAreaLayoutGuide) + } + } + + private func bindData() { + // 캘린더 데이터 바인딩 + viewModel.diaryCalendarSubject + .observe(on: MainScheduler.instance) + .subscribe(onNext: { [weak self] calendarData in + self?.updateDiaryTodayView(with: calendarData) + }) + .disposed(by: disposeBag) + + // 캘린더 에러 처리 + viewModel.diaryCalendarErrorSubject + .observe(on: MainScheduler.instance) + .subscribe(onNext: { error in + print("❌ 캘린더 데이터 로드 실패: \(error)") + }) + .disposed(by: disposeBag) + + // 특정 날짜 일기 데이터 바인딩 + viewModel.diarySubject + .observe(on: MainScheduler.instance) + .subscribe(onNext: { [weak self] diary in + print("📱 DiaryViewController: diarySubject 수신됨 - \(diary)") + self?.updateDiaryTodayViewWithSelectedDate(diary: diary) + }) + .disposed(by: disposeBag) + + // 특정 날짜 일기 에러 처리 + viewModel.diaryErrorSubject + .observe(on: MainScheduler.instance) + .subscribe(onNext: { error in + print("❌ 특정 날짜 일기 로드 실패: \(error)") + }) + .disposed(by: disposeBag) + + // 특정 날짜에 일기가 없을 때 처리 + viewModel.diaryEmptySubject + .observe(on: MainScheduler.instance) + .subscribe(onNext: { [weak self] date in + self?.updateDiaryTodayViewWithEmptyData(date: date) + }) + .disposed(by: disposeBag) + } + + private func bindEvents() { + diaryView.onAddButtonTapped = { [weak self] in + self?.presentNewDiaryView() + } + + // DiaryTodayView의 일기 추가 버튼 이벤트 바인딩 + diaryView.diaryTodayView.tapDiaryAdd = { [weak self] in + self?.presentNewDiaryView() + } + + // DiaryTodayView의 일기 상세 보기 이벤트 바인딩 + diaryView.diaryTodayView.tapDiaryDetail = { [weak self] diaryData in + if let diaryData = diaryData { + self?.presentDiaryDetailView(diaryData: diaryData) + } + } + + // 캘린더 날짜 클릭 이벤트 바인딩 + diaryView.calendarView.tapDay = { [weak self] date in + self?.viewModel.getDiary(date: date) + } + + // 월 변경 이벤트 바인딩 + diaryView.tapPrevious = { [weak self] year, month in + self?.viewModel.getDiaryCalendar(year: year, month: month) + } + + diaryView.tapNext = { [weak self] year, month in + self?.viewModel.getDiaryCalendar(year: year, month: month) + } + } + + private func setupInitialState() { + // 첫 진입 시 오늘 날짜로 DiaryTodayView 초기 상태 설정 + let today = Date() + diaryView.diaryTodayView.setDiaryTodayEmptyData(date: today) + + // 캘린더에서 오늘 날짜 선택 상태로 설정 + diaryView.calendarView.setSelectedDate(today) + } + + private func presentNewDiaryView() { + let diaryAddViewController = DiaryAddViewController(diaryViewModel: viewModel) + self.navigationController?.pushViewController(diaryAddViewController, animated: true) + } + + private func presentDiaryDetailView(diaryData: DiaryVO) { + let diaryDetailViewController = DiaryDetailViewController(diaryViewModel: viewModel, diaryData: diaryData) + diaryDetailViewController.hidesBottomBarWhenPushed = true + self.navigationController?.pushViewController(diaryDetailViewController, animated: true) + } + + private func updateDiaryTodayView(with calendarData: DiaryCalendarVO) { + // 캘린더 데이터로 이모지 정보 업데이트 + var emotionData: [Int: String] = [:] + for diary in calendarData.diaryList { + let formatter = DateFormatter() + formatter.dateFormat = "yyyy-MM-dd" + if let date = formatter.date(from: diary.createdAt) { + let day = Calendar.current.component(.day, from: date) + // 점수에 따른 이모지 매핑 (예시) + let emotion = getEmotionForScore(diary.score) + emotionData[day] = emotion + } + } + diaryView.calendarView.setEmotionData(emotionData) + + // 첫 진입 시에만 오늘 날짜로 DiaryTodayView 업데이트 + guard isFirstAppearance else { return } + + let today = Date() + let formatter = DateFormatter() + formatter.dateFormat = "yyyy-MM-dd" + let todayString = formatter.string(from: today) + + // 오늘 날짜의 일기가 있는지 확인 + let todayDiary = calendarData.diaryList.first { diary in + diary.createdAt.hasPrefix(todayString) + } + + if let diary = todayDiary { + // 오늘 일기가 있는 경우 + diaryView.diaryTodayView.setDiaryTodayData( + date: todayString, + diaryId: diary.diaryId, + score: diary.score, + content: diary.createdAt + ) + } else { + // 오늘 일기가 없는 경우 - 오늘 날짜로 빈 상태 설정 + diaryView.diaryTodayView.setDiaryTodayEmptyData(date: today) + } + } + + private func getEmotionForScore(_ score: Int) -> String { + switch score { + case 90...100: + return "🥳" // 파티 모자 + case 80..<90: + return "😊" // 웃는 얼굴 + case 70..<80: + return "😐" // 무표정한 얼굴 + case 60..<70: + return "😕" // 약간 찡그린 얼굴 + default: + return "😢" // 우는 얼굴 + } + } + + private func updateDiaryTodayViewWithSelectedDate(diary: DiaryVO) { + print("📱 updateDiaryTodayViewWithSelectedDate 호출됨") + print("📱 diary.createdAt: \(diary.createdAt)") + print("📱 diary.score: \(diary.spellingDto.score)") + print("📱 diary.content: \(diary.spellingDto.revisedContent)") + + // 선택된 날짜의 일기 데이터로 DiaryTodayView 업데이트 + let formatter = DateFormatter() + formatter.dateFormat = "yyyy년 MM월 dd일" + if let date = formatter.date(from: diary.createdAt) { + print("📱 Date 파싱 성공: \(date)") + diaryView.diaryTodayView.setDiaryTodayData( + date: date, + diaryId: diary.diaryId, + score: diary.spellingDto.score, + content: diary.spellingDto.revisedContent + ) + } else { + print("📱 Date 파싱 실패, 문자열로 직접 설정") + // 날짜 파싱 실패 시 문자열로 직접 설정 + diaryView.diaryTodayView.setDiaryTodayData( + date: diary.createdAt, + diaryId: diary.diaryId, + score: diary.spellingDto.score, + content: diary.spellingDto.revisedContent + ) + } + + // DiaryVO 데이터 설정 (탭 이벤트용) + diaryView.diaryTodayView.setDiaryData(diary) + } + + private func updateDiaryTodayViewWithEmptyData(date: Date) { + print("📱 updateDiaryTodayViewWithEmptyData 호출됨") + print("📱 date: \(date)") + print("📱 오늘 날짜인가: \(Calendar.current.isDateInToday(date))") + + // 선택된 날짜에 일기가 없을 때 DiaryTodayView 업데이트 + diaryView.diaryTodayView.setDiaryTodayEmptyData(date: date) + } +} diff --git a/Projects/Diary/Sources/ViewModel/DiaryViewModel.swift b/Projects/Diary/Sources/ViewModel/DiaryViewModel.swift index 42c9e58..0410428 100644 --- a/Projects/Diary/Sources/ViewModel/DiaryViewModel.swift +++ b/Projects/Diary/Sources/ViewModel/DiaryViewModel.swift @@ -5,4 +5,90 @@ // Created by 박지윤 on 7/1/25. // +import Domain +import RxSwift +import Foundation +protocol DiaryViewModelProtocol { + func postDiary(content: String) + func getDiary(date: Date) + func getDiaryDetail(diaryId: Int, date: Date) + func deleteDiaryDetail(diaryId: Int) + func getDiaryCalendar(year: Int, month: Int) +} + +public class DiaryViewModel: DiaryViewModelProtocol { + private let disposeBag = DisposeBag() + private let diaryUseCase: DiaryUseCase + private let tokenUseCase: TokenUseCase + + public var onDiaryPostSuccess: ((DiaryVO) -> Void)? + public var onDiaryPostFailure: ((Error) -> Void)? + + // 캘린더 데이터 Subject + public let diaryCalendarSubject = PublishSubject() + public let diaryCalendarErrorSubject = PublishSubject() + + // 특정 날짜 일기 데이터 Subject + public let diarySubject = PublishSubject() + public let diaryErrorSubject = PublishSubject() + public let diaryEmptySubject = PublishSubject() // 일기가 없을 때 날짜 전달 + + public init(diaryUseCase: DiaryUseCase, + tokenUseCase: TokenUseCase) { + self.diaryUseCase = diaryUseCase + self.tokenUseCase = tokenUseCase + } + + func postDiary(content: String) { + diaryUseCase.postDiary(content: content) + .subscribe(onSuccess: { [weak self] data in + print("✅ postDiary 성공: \(data)") + self?.onDiaryPostSuccess?(data) + }, onFailure: { [weak self] error in + print("❌ postDiary 실패: \(error)") + self?.onDiaryPostFailure?(error) + }).disposed(by: disposeBag) + } + + func getDiary(date: Date) { + diaryUseCase.getDiary(date: date) + .subscribe(onSuccess: { [weak self] data in + print("✅ getDiary 성공: \(data)") + self?.diarySubject.onNext(data) + }, onFailure: { [weak self] error in + print("❌ getDiary 실패: \(error)") + // 에러 시 해당 날짜에 일기가 없다고 처리 + self?.diaryEmptySubject.onNext(date) + }).disposed(by: disposeBag) + } + + func getDiaryDetail(diaryId: Int, date: Date) { + diaryUseCase.getDiaryDetail(diaryId: diaryId, date: date) + .subscribe(onSuccess: { data in + print("✅ getDiaryDetail 성공: \(data)") + }, onFailure: { error in + print("❌ getDiaryDetail 실패: \(error)") + }).disposed(by: disposeBag) + } + + func deleteDiaryDetail(diaryId: Int) { + diaryUseCase.deleteDiaryDetail(diaryId: diaryId) + .subscribe(onSuccess: { result in + print("✅ deleteDiaryDetail 성공: \(result)") + }, onFailure: { error in + print("❌ deleteDiaryDetail 실패: \(error)") + }).disposed(by: disposeBag) + } + + func getDiaryCalendar(year: Int, month: Int) { + diaryUseCase.getDiaryCalendar(year: year, month: month) + .subscribe(onSuccess: { [weak self] data in + print("✅ getDiaryCalendar 성공: \(data)") + self?.diaryCalendarSubject.onNext(data) + }, onFailure: { [weak self] error in + print("❌ getDiaryCalendar 실패: \(error)") + self?.diaryCalendarErrorSubject.onNext(error) + }).disposed(by: disposeBag) + } +} diff --git a/Projects/Domain/Domain.xcodeproj/project.pbxproj b/Projects/Domain/Domain.xcodeproj/project.pbxproj index 52beb99..80e7544 100644 --- a/Projects/Domain/Domain.xcodeproj/project.pbxproj +++ b/Projects/Domain/Domain.xcodeproj/project.pbxproj @@ -8,22 +8,27 @@ /* Begin PBXBuildFile section */ 0BCC97781DA6EE874B081DBE /* RxSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9A2E30672E510822AAF38EAD /* RxSwift.framework */; }; + 0C6B16A92D1488D0C06BCD13 /* SignRepository.swift in Sources */ = {isa = PBXBuildFile; fileRef = B437170C7511425C025E4D08 /* SignRepository.swift */; }; 0DED9075FE34124C093D9FDF /* LoginVO.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD08A7186FB9676854B7AEAC /* LoginVO.swift */; }; + 16748FBAA3739FB31DEB293A /* UIImage+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6DBBF70005BD9279B31ACC74 /* UIImage+Extension.swift */; }; 1782D2A1A7FB2BD6FFA1DFEA /* QuizRepository.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1BA1471CB01ECE73BAD3D595 /* QuizRepository.swift */; }; + 36AF21246B6104BC68087D5E /* SignUseCase.swift in Sources */ = {isa = PBXBuildFile; fileRef = A89402CD621AD4E2A5B16175 /* SignUseCase.swift */; }; 3E835D4F2E5F639557AC7C06 /* TokenUseCase.swift in Sources */ = {isa = PBXBuildFile; fileRef = ABB8C62A6FE12783AD3819BE /* TokenUseCase.swift */; }; + 51C33E60507A25394BA1A51D /* ChatUseCase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9B2CBEE7AC445460FAE28675 /* ChatUseCase.swift */; }; + 58F522AE126B20D41BB813F2 /* LoginUseCase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A7201F0D687DD47D9F942A4 /* LoginUseCase.swift */; }; 5EB8590C9D2C002D0BDBC021 /* CourseVO.swift in Sources */ = {isa = PBXBuildFile; fileRef = 747DBBCAB797E5E0A82177F2 /* CourseVO.swift */; }; + 5F1581531DDCFDC2FD2DA68C /* DefaultVO.swift in Sources */ = {isa = PBXBuildFile; fileRef = F710AE286B183E82CCF5A177 /* DefaultVO.swift */; }; 69DAD609572D32F2BA3845AE /* String+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2E1DA9A49E79A791F7A526A1 /* String+Extension.swift */; }; + 6A879D6F3FB2FB826037933B /* ChatVO.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00CA0A9509EC3798653BF795 /* ChatVO.swift */; }; + 6D73D9043F7C3AA14AED7068 /* ChatRepository.swift in Sources */ = {isa = PBXBuildFile; fileRef = CFB20675E9BA27548003EA55 /* ChatRepository.swift */; }; + 72077A10B09B16423036D020 /* ChatRoomListVO.swift in Sources */ = {isa = PBXBuildFile; fileRef = 89BF735832A6028B0F0633B0 /* ChatRoomListVO.swift */; }; 92BD46EE48F6C63B6E43D069 /* UIView+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32AB908BB15CBDA006ADC3A1 /* UIView+Extension.swift */; }; - 950A0D692E5CCBB900C07CF2 /* SignUseCase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 950A0D682E5CCBB400C07CF2 /* SignUseCase.swift */; }; - 950A0D6B2E5CCBF000C07CF2 /* SignRepository.swift in Sources */ = {isa = PBXBuildFile; fileRef = 950A0D6A2E5CCBE800C07CF2 /* SignRepository.swift */; }; - 950A0D822E5DE10C00C07CF2 /* LoginUseCase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 950A0D812E5DE10700C07CF2 /* LoginUseCase.swift */; }; - 950A0D922E603DA100C07CF2 /* DefaultVO.swift in Sources */ = {isa = PBXBuildFile; fileRef = 950A0D912E603D9C00C07CF2 /* DefaultVO.swift */; }; - 9516ED592E71CE1300F548A1 /* ChatDetailVO.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9516ED582E71CE1000F548A1 /* ChatDetailVO.swift */; }; - 9516ED5B2E71CF0300F548A1 /* ChatRoomListVO.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9516ED5A2E71CEFE00F548A1 /* ChatRoomListVO.swift */; }; - 951F3F892E6DE1F80022583B /* ChatUseCase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 951F3F882E6DE1F60022583B /* ChatUseCase.swift */; }; - 951F3F8B2E6DE2140022583B /* ChatRepository.swift in Sources */ = {isa = PBXBuildFile; fileRef = 951F3F8A2E6DE2100022583B /* ChatRepository.swift */; }; - 951F3F8D2E6F360C0022583B /* ChatVO.swift in Sources */ = {isa = PBXBuildFile; fileRef = 951F3F8C2E6F36090022583B /* ChatVO.swift */; }; - 95369A7E2E28B8D9000C893F /* UIImage+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 95369A7D2E28B8D1000C893F /* UIImage+Extension.swift */; }; + 9532C7B02E7872A400B4BADE /* DiaryVO.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9532C7AF2E7872A100B4BADE /* DiaryVO.swift */; }; + 9532C7B22E78F44800B4BADE /* DiaryCalendarVO.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9532C7B12E78F44400B4BADE /* DiaryCalendarVO.swift */; }; + 956C4D8B2E77104800E32F93 /* DiaryUseCase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 956C4D8A2E77104500E32F93 /* DiaryUseCase.swift */; }; + 956C4D8D2E7710BC00E32F93 /* DiaryRepository.swift in Sources */ = {isa = PBXBuildFile; fileRef = 956C4D8C2E7710B900E32F93 /* DiaryRepository.swift */; }; + 956C4D922E771DD300E32F93 /* TokenValidationUseCase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 956C4D912E771DCB00E32F93 /* TokenValidationUseCase.swift */; }; + 9DBF2CB00F16FBC09602488A /* ChatDetailVO.swift in Sources */ = {isa = PBXBuildFile; fileRef = 097F5EF085884AD8C9591CE5 /* ChatDetailVO.swift */; }; A0CEC5A2E5A76EBD987CE37D /* StepVO.swift in Sources */ = {isa = PBXBuildFile; fileRef = 798CFC331192C9FE77F3446A /* StepVO.swift */; }; B0B0382EF5DFDACDD3EB8A75 /* QuizUseCase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F4565F6A02AFBF3D9A0D41B /* QuizUseCase.swift */; }; BBED9ACB62B271298F7028F8 /* CourseUseCase.swift in Sources */ = {isa = PBXBuildFile; fileRef = F904C836B5CD2D4DA5EAE38D /* CourseUseCase.swift */; }; @@ -47,29 +52,34 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ + 00CA0A9509EC3798653BF795 /* ChatVO.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatVO.swift; sourceTree = ""; }; + 097F5EF085884AD8C9591CE5 /* ChatDetailVO.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatDetailVO.swift; sourceTree = ""; }; 1A1F66123A81636A913985E6 /* TokenRepository.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TokenRepository.swift; sourceTree = ""; }; 1BA1471CB01ECE73BAD3D595 /* QuizRepository.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QuizRepository.swift; sourceTree = ""; }; 24F9958F3DAC8013B440CEEA /* QuizVO.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QuizVO.swift; sourceTree = ""; }; 2E1DA9A49E79A791F7A526A1 /* String+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "String+Extension.swift"; sourceTree = ""; }; 32AB908BB15CBDA006ADC3A1 /* UIView+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIView+Extension.swift"; sourceTree = ""; }; + 4A7201F0D687DD47D9F942A4 /* LoginUseCase.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoginUseCase.swift; sourceTree = ""; }; + 6DBBF70005BD9279B31ACC74 /* UIImage+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIImage+Extension.swift"; sourceTree = ""; }; 6F4565F6A02AFBF3D9A0D41B /* QuizUseCase.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QuizUseCase.swift; sourceTree = ""; }; 747DBBCAB797E5E0A82177F2 /* CourseVO.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CourseVO.swift; sourceTree = ""; }; 798CFC331192C9FE77F3446A /* StepVO.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StepVO.swift; sourceTree = ""; }; + 89BF735832A6028B0F0633B0 /* ChatRoomListVO.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatRoomListVO.swift; sourceTree = ""; }; 8A2AB67DE6AA50B3229C7A86 /* LoginRepository.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoginRepository.swift; sourceTree = ""; }; - 950A0D682E5CCBB400C07CF2 /* SignUseCase.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SignUseCase.swift; sourceTree = ""; }; - 950A0D6A2E5CCBE800C07CF2 /* SignRepository.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SignRepository.swift; sourceTree = ""; }; - 950A0D812E5DE10700C07CF2 /* LoginUseCase.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoginUseCase.swift; sourceTree = ""; }; - 950A0D912E603D9C00C07CF2 /* DefaultVO.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DefaultVO.swift; sourceTree = ""; }; - 9516ED582E71CE1000F548A1 /* ChatDetailVO.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatDetailVO.swift; sourceTree = ""; }; - 9516ED5A2E71CEFE00F548A1 /* ChatRoomListVO.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatRoomListVO.swift; sourceTree = ""; }; - 951F3F882E6DE1F60022583B /* ChatUseCase.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatUseCase.swift; sourceTree = ""; }; - 951F3F8A2E6DE2100022583B /* ChatRepository.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatRepository.swift; sourceTree = ""; }; - 951F3F8C2E6F36090022583B /* ChatVO.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatVO.swift; sourceTree = ""; }; - 95369A7D2E28B8D1000C893F /* UIImage+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIImage+Extension.swift"; sourceTree = ""; }; + 9532C7AF2E7872A100B4BADE /* DiaryVO.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DiaryVO.swift; sourceTree = ""; }; + 9532C7B12E78F44400B4BADE /* DiaryCalendarVO.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DiaryCalendarVO.swift; sourceTree = ""; }; + 956C4D8A2E77104500E32F93 /* DiaryUseCase.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DiaryUseCase.swift; sourceTree = ""; }; + 956C4D8C2E7710B900E32F93 /* DiaryRepository.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DiaryRepository.swift; sourceTree = ""; }; + 956C4D912E771DCB00E32F93 /* TokenValidationUseCase.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TokenValidationUseCase.swift; sourceTree = ""; }; 9A2E30672E510822AAF38EAD /* RxSwift.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = RxSwift.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 9B2CBEE7AC445460FAE28675 /* ChatUseCase.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatUseCase.swift; sourceTree = ""; }; + A89402CD621AD4E2A5B16175 /* SignUseCase.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SignUseCase.swift; sourceTree = ""; }; ABB8C62A6FE12783AD3819BE /* TokenUseCase.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TokenUseCase.swift; sourceTree = ""; }; + B437170C7511425C025E4D08 /* SignRepository.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SignRepository.swift; sourceTree = ""; }; BD15A37E85BAE3A36B2CE72C /* Domain-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = "Domain-Info.plist"; sourceTree = ""; }; + CFB20675E9BA27548003EA55 /* ChatRepository.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatRepository.swift; sourceTree = ""; }; DCA3E06ED2A8180A63919B6C /* CourseRepository.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CourseRepository.swift; sourceTree = ""; }; + F710AE286B183E82CCF5A177 /* DefaultVO.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DefaultVO.swift; sourceTree = ""; }; F904C836B5CD2D4DA5EAE38D /* CourseUseCase.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CourseUseCase.swift; sourceTree = ""; }; FD08A7186FB9676854B7AEAC /* LoginVO.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoginVO.swift; sourceTree = ""; }; FD49F277B86481D3D105FAC8 /* Domain.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Domain.framework; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -107,8 +117,8 @@ 266DBF54466AF3D041290301 /* Extension */ = { isa = PBXGroup; children = ( - 95369A7D2E28B8D1000C893F /* UIImage+Extension.swift */, 2E1DA9A49E79A791F7A526A1 /* String+Extension.swift */, + 6DBBF70005BD9279B31ACC74 /* UIImage+Extension.swift */, 32AB908BB15CBDA006ADC3A1 /* UIView+Extension.swift */, ); path = Extension; @@ -128,8 +138,10 @@ 2BE278B80FDD95404961E580 /* UseCase */ = { isa = PBXGroup; children = ( - 951F3F882E6DE1F60022583B /* ChatUseCase.swift */, - 950A0D672E5CCBAC00C07CF2 /* Login */, + 956C4D912E771DCB00E32F93 /* TokenValidationUseCase.swift */, + 91F12B6BCED9685E9C73515E /* Login */, + 9B2CBEE7AC445460FAE28675 /* ChatUseCase.swift */, + 956C4D8A2E77104500E32F93 /* DiaryUseCase.swift */, F904C836B5CD2D4DA5EAE38D /* CourseUseCase.swift */, 6F4565F6A02AFBF3D9A0D41B /* QuizUseCase.swift */, ABB8C62A6FE12783AD3819BE /* TokenUseCase.swift */, @@ -154,11 +166,11 @@ name = Project; sourceTree = ""; }; - 950A0D672E5CCBAC00C07CF2 /* Login */ = { + 91F12B6BCED9685E9C73515E /* Login */ = { isa = PBXGroup; children = ( - 950A0D812E5DE10700C07CF2 /* LoginUseCase.swift */, - 950A0D682E5CCBB400C07CF2 /* SignUseCase.swift */, + 4A7201F0D687DD47D9F942A4 /* LoginUseCase.swift */, + A89402CD621AD4E2A5B16175 /* SignUseCase.swift */, ); path = Login; sourceTree = ""; @@ -166,11 +178,13 @@ 9C17CEB3595F4C3FA69C5BAF /* VO */ = { isa = PBXGroup; children = ( - 951F3F8C2E6F36090022583B /* ChatVO.swift */, - 9516ED5A2E71CEFE00F548A1 /* ChatRoomListVO.swift */, - 9516ED582E71CE1000F548A1 /* ChatDetailVO.swift */, - 950A0D912E603D9C00C07CF2 /* DefaultVO.swift */, + 9532C7AF2E7872A100B4BADE /* DiaryVO.swift */, + 9532C7B12E78F44400B4BADE /* DiaryCalendarVO.swift */, + 097F5EF085884AD8C9591CE5 /* ChatDetailVO.swift */, + 89BF735832A6028B0F0633B0 /* ChatRoomListVO.swift */, + 00CA0A9509EC3798653BF795 /* ChatVO.swift */, 747DBBCAB797E5E0A82177F2 /* CourseVO.swift */, + F710AE286B183E82CCF5A177 /* DefaultVO.swift */, FD08A7186FB9676854B7AEAC /* LoginVO.swift */, 24F9958F3DAC8013B440CEEA /* QuizVO.swift */, 798CFC331192C9FE77F3446A /* StepVO.swift */, @@ -181,11 +195,12 @@ BC1A55AF7DC675AFEA6E7C12 /* RepositoryProtocol */ = { isa = PBXGroup; children = ( - 951F3F8A2E6DE2100022583B /* ChatRepository.swift */, - 950A0D6A2E5CCBE800C07CF2 /* SignRepository.swift */, + CFB20675E9BA27548003EA55 /* ChatRepository.swift */, + 956C4D8C2E7710B900E32F93 /* DiaryRepository.swift */, DCA3E06ED2A8180A63919B6C /* CourseRepository.swift */, 8A2AB67DE6AA50B3229C7A86 /* LoginRepository.swift */, 1BA1471CB01ECE73BAD3D595 /* QuizRepository.swift */, + B437170C7511425C025E4D08 /* SignRepository.swift */, 1A1F66123A81636A913985E6 /* TokenRepository.swift */, ); path = RepositoryProtocol; @@ -263,29 +278,34 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 950A0D6B2E5CCBF000C07CF2 /* SignRepository.swift in Sources */, 69DAD609572D32F2BA3845AE /* String+Extension.swift in Sources */, - 950A0D822E5DE10C00C07CF2 /* LoginUseCase.swift in Sources */, - 9516ED592E71CE1300F548A1 /* ChatDetailVO.swift in Sources */, + 16748FBAA3739FB31DEB293A /* UIImage+Extension.swift in Sources */, 92BD46EE48F6C63B6E43D069 /* UIView+Extension.swift in Sources */, - 9516ED5B2E71CF0300F548A1 /* ChatRoomListVO.swift in Sources */, + 6D73D9043F7C3AA14AED7068 /* ChatRepository.swift in Sources */, + 9532C7B02E7872A400B4BADE /* DiaryVO.swift in Sources */, D510BC17C4583615CB60439E /* CourseRepository.swift in Sources */, D7012CC494E56CD8CE58176F /* LoginRepository.swift in Sources */, 1782D2A1A7FB2BD6FFA1DFEA /* QuizRepository.swift in Sources */, + 9532C7B22E78F44800B4BADE /* DiaryCalendarVO.swift in Sources */, + 0C6B16A92D1488D0C06BCD13 /* SignRepository.swift in Sources */, D81C7F2583A1BD30BD7C53DD /* TokenRepository.swift in Sources */, + 51C33E60507A25394BA1A51D /* ChatUseCase.swift in Sources */, BBED9ACB62B271298F7028F8 /* CourseUseCase.swift in Sources */, + 58F522AE126B20D41BB813F2 /* LoginUseCase.swift in Sources */, + 36AF21246B6104BC68087D5E /* SignUseCase.swift in Sources */, B0B0382EF5DFDACDD3EB8A75 /* QuizUseCase.swift in Sources */, 3E835D4F2E5F639557AC7C06 /* TokenUseCase.swift in Sources */, + 956C4D8B2E77104800E32F93 /* DiaryUseCase.swift in Sources */, + 9DBF2CB00F16FBC09602488A /* ChatDetailVO.swift in Sources */, + 72077A10B09B16423036D020 /* ChatRoomListVO.swift in Sources */, + 6A879D6F3FB2FB826037933B /* ChatVO.swift in Sources */, + 956C4D8D2E7710BC00E32F93 /* DiaryRepository.swift in Sources */, + 956C4D922E771DD300E32F93 /* TokenValidationUseCase.swift in Sources */, 5EB8590C9D2C002D0BDBC021 /* CourseVO.swift in Sources */, - 951F3F8D2E6F360C0022583B /* ChatVO.swift in Sources */, - 951F3F892E6DE1F80022583B /* ChatUseCase.swift in Sources */, - 950A0D922E603DA100C07CF2 /* DefaultVO.swift in Sources */, + 5F1581531DDCFDC2FD2DA68C /* DefaultVO.swift in Sources */, 0DED9075FE34124C093D9FDF /* LoginVO.swift in Sources */, - 95369A7E2E28B8D9000C893F /* UIImage+Extension.swift in Sources */, - 950A0D692E5CCBB900C07CF2 /* SignUseCase.swift in Sources */, CFBF3D58082C5F201FCFFD5B /* QuizVO.swift in Sources */, A0CEC5A2E5A76EBD987CE37D /* StepVO.swift in Sources */, - 951F3F8B2E6DE2140022583B /* ChatRepository.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -369,7 +389,7 @@ SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; SWIFT_COMPILATION_MODE = wholemodule; - SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; + SWIFT_OPTIMIZATION_LEVEL = "-O"; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; VERSIONING_SYSTEM = "apple-generic"; diff --git a/Projects/Domain/Sources/RepositoryProtocol/DiaryRepository.swift b/Projects/Domain/Sources/RepositoryProtocol/DiaryRepository.swift new file mode 100644 index 0000000..7023641 --- /dev/null +++ b/Projects/Domain/Sources/RepositoryProtocol/DiaryRepository.swift @@ -0,0 +1,17 @@ +// +// DiaryRepository.swift +// Domain +// +// Created by 박지윤 on 9/15/25. +// + +import RxSwift +import Foundation + +public protocol DiaryRepository { + func postDiary(content: String) -> Single + func getDiary(date: Date) -> Single + func getDiaryDetail(diaryId: Int, date: Date) -> Single + func deleteDiaryDetail(diaryId: Int) -> Single + func getDiaryCalendar(year: Int, month: Int) -> Single +} diff --git a/Projects/Domain/Sources/RepositoryProtocol/TokenRepository.swift b/Projects/Domain/Sources/RepositoryProtocol/TokenRepository.swift index 19a27af..2b14313 100644 --- a/Projects/Domain/Sources/RepositoryProtocol/TokenRepository.swift +++ b/Projects/Domain/Sources/RepositoryProtocol/TokenRepository.swift @@ -14,4 +14,5 @@ public protocol TokenRepository { func saveRefreshToken(token: String) func getRefreshToken() -> String? func clearRefreshToken() + func validateToken() -> Single } diff --git a/Projects/Domain/Sources/UseCase/DiaryUseCase.swift b/Projects/Domain/Sources/UseCase/DiaryUseCase.swift new file mode 100644 index 0000000..92daed3 --- /dev/null +++ b/Projects/Domain/Sources/UseCase/DiaryUseCase.swift @@ -0,0 +1,45 @@ +// +// DiaryUseCase.swift +// Domain +// +// Created by 박지윤 on 9/15/25. +// + +import RxSwift +import Foundation + +public protocol DiaryUseCase { + func postDiary(content: String) -> Single + func getDiary(date: Date) -> Single + func getDiaryDetail(diaryId: Int, date: Date) -> Single + func deleteDiaryDetail(diaryId: Int) -> Single + func getDiaryCalendar(year: Int, month: Int) -> Single +} + +public final class DefaultDiaryUseCase: DiaryUseCase { + private let repository: DiaryRepository + + public init(repository: DiaryRepository) { + self.repository = repository + } + + public func postDiary(content: String) -> Single { + repository.postDiary(content: content) + } + + public func getDiary(date: Date) -> Single { + repository.getDiary(date: date) + } + + public func getDiaryDetail(diaryId: Int, date: Date) -> Single { + repository.getDiaryDetail(diaryId: diaryId, date: date) + } + + public func deleteDiaryDetail(diaryId: Int) -> Single { + repository.deleteDiaryDetail(diaryId: diaryId) + } + + public func getDiaryCalendar(year: Int, month: Int) -> Single { + repository.getDiaryCalendar(year: year, month: month) + } +} diff --git a/Projects/Domain/Sources/UseCase/TokenValidationUseCase.swift b/Projects/Domain/Sources/UseCase/TokenValidationUseCase.swift new file mode 100644 index 0000000..5bd20e1 --- /dev/null +++ b/Projects/Domain/Sources/UseCase/TokenValidationUseCase.swift @@ -0,0 +1,24 @@ +// +// TokenValidationUseCase.swift +// Domain +// +// Created by 박지윤 on 9/15/25. +// + +import RxSwift + +public protocol TokenValidationUseCase { + func validateToken() -> Single +} + +public final class DefaultTokenValidationUseCase: TokenValidationUseCase { + private let repository: TokenRepository + + public init(repository: TokenRepository) { + self.repository = repository + } + + public func validateToken() -> Single { + return repository.validateToken() + } +} diff --git a/Projects/Domain/Sources/VO/DiaryCalendarVO.swift b/Projects/Domain/Sources/VO/DiaryCalendarVO.swift new file mode 100644 index 0000000..bd24510 --- /dev/null +++ b/Projects/Domain/Sources/VO/DiaryCalendarVO.swift @@ -0,0 +1,35 @@ +// +// DiaryCalendarVO.swift +// Domain +// +// Created by 박지윤 on 9/16/25. +// + + +public struct DiaryCalendarVO { + public let year: Int + public let month: Int + public let diaryList: [DiaryListVO] + + public init(year: Int, + month: Int, + diaryList: [DiaryListVO]) { + self.year = year + self.month = month + self.diaryList = diaryList + } +} + +public struct DiaryListVO { + public let diaryId: Int + public let createdAt: String + public let score: Int + + public init(diaryId: Int, + createdAt: String, + score: Int) { + self.diaryId = diaryId + self.createdAt = createdAt + self.score = score + } +} diff --git a/Projects/Domain/Sources/VO/DiaryVO.swift b/Projects/Domain/Sources/VO/DiaryVO.swift new file mode 100644 index 0000000..9ff2401 --- /dev/null +++ b/Projects/Domain/Sources/VO/DiaryVO.swift @@ -0,0 +1,63 @@ +// +// DiaryVO.swift +// Domain +// +// Created by 박지윤 on 9/16/25. +// + +public struct DiaryVO { + public let diaryId: Int + public let createdAt: String + public let originContent: String + public let spellingDto: SpellingVO + public let feedback: String + + public init(diaryId: Int, + createdAt: String, + originContent: String, + spellingDto: SpellingVO, + feedback: String) { + self.diaryId = diaryId + self.createdAt = createdAt + self.originContent = originContent + self.spellingDto = spellingDto + self.feedback = feedback + } +} + +public struct SpellingVO { + public let revisedContent: String + public let score: Int + public let revisions: [RevisionVO] + + public init(revisedContent: String, + score: Int, + revisions: [RevisionVO]) { + self.revisedContent = revisedContent + self.score = score + self.revisions = revisions + } +} + +public struct RevisionVO { + public let originContent: String + public let revisedContent: String + public let beginOffset: Int + public let comment: String + public let category: String + public let examples: [String] + + public init(originContent: String, + revisedContent: String, + beginOffset: Int, + comment: String, + category: String, + examples: [String]) { + self.originContent = originContent + self.revisedContent = revisedContent + self.beginOffset = beginOffset + self.comment = comment + self.category = category + self.examples = examples + } +} diff --git a/Projects/Home/Home.xcodeproj/project.pbxproj b/Projects/Home/Home.xcodeproj/project.pbxproj index 375dc06..49de54c 100644 --- a/Projects/Home/Home.xcodeproj/project.pbxproj +++ b/Projects/Home/Home.xcodeproj/project.pbxproj @@ -323,6 +323,7 @@ DYLIB_INSTALL_NAME_BASE = "@rpath"; HEADER_SEARCH_PATHS = ( "$(inherited)", + "$(SRCROOT)/../../Tuist/.build/checkouts/FSCalendar/FSCalendar/include", "$(SRCROOT)/../../Tuist/.build/checkouts/RxSwift/Sources/RxCocoaRuntime/include", ); INFOPLIST_FILE = "Derived/InfoPlists/Home-Info.plist"; @@ -334,11 +335,14 @@ ); OTHER_CFLAGS = ( "$(inherited)", + "-fmodule-map-file=$(SRCROOT)/../../Tuist/.build/checkouts/FSCalendar/FSCalendar/include/module.modulemap", "-fmodule-map-file=$(SRCROOT)/../../Tuist/.build/tuist-derived/RxCocoaRuntime/RxCocoaRuntime.modulemap", ); OTHER_SWIFT_FLAGS = ( "$(inherited)", "-Xcc", + "-fmodule-map-file=$(SRCROOT)/../../Tuist/.build/checkouts/FSCalendar/FSCalendar/include/module.modulemap", + "-Xcc", "-fmodule-map-file=$(SRCROOT)/../../Tuist/.build/tuist-derived/RxCocoaRuntime/RxCocoaRuntime.modulemap", ); PRODUCT_BUNDLE_IDENTIFIER = io.tuist.Home; @@ -431,6 +435,7 @@ DYLIB_INSTALL_NAME_BASE = "@rpath"; HEADER_SEARCH_PATHS = ( "$(inherited)", + "$(SRCROOT)/../../Tuist/.build/checkouts/FSCalendar/FSCalendar/include", "$(SRCROOT)/../../Tuist/.build/checkouts/RxSwift/Sources/RxCocoaRuntime/include", ); INFOPLIST_FILE = "Derived/InfoPlists/Home-Info.plist"; @@ -442,11 +447,14 @@ ); OTHER_CFLAGS = ( "$(inherited)", + "-fmodule-map-file=$(SRCROOT)/../../Tuist/.build/checkouts/FSCalendar/FSCalendar/include/module.modulemap", "-fmodule-map-file=$(SRCROOT)/../../Tuist/.build/tuist-derived/RxCocoaRuntime/RxCocoaRuntime.modulemap", ); OTHER_SWIFT_FLAGS = ( "$(inherited)", "-Xcc", + "-fmodule-map-file=$(SRCROOT)/../../Tuist/.build/checkouts/FSCalendar/FSCalendar/include/module.modulemap", + "-Xcc", "-fmodule-map-file=$(SRCROOT)/../../Tuist/.build/tuist-derived/RxCocoaRuntime/RxCocoaRuntime.modulemap", ); PRODUCT_BUNDLE_IDENTIFIER = io.tuist.Home; @@ -457,7 +465,7 @@ SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; SWIFT_COMPILATION_MODE = wholemodule; - SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; + SWIFT_OPTIMIZATION_LEVEL = "-O"; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; VERSIONING_SYSTEM = "apple-generic"; @@ -471,6 +479,7 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; HEADER_SEARCH_PATHS = ( "$(inherited)", + "$(SRCROOT)/../../Tuist/.build/checkouts/FSCalendar/FSCalendar/include", "$(SRCROOT)/../../Tuist/.build/checkouts/RxSwift/Sources/RxCocoaRuntime/include", ); INFOPLIST_FILE = "Derived/InfoPlists/HomeTests-Info.plist"; @@ -481,11 +490,14 @@ ); OTHER_CFLAGS = ( "$(inherited)", + "-fmodule-map-file=$(SRCROOT)/../../Tuist/.build/checkouts/FSCalendar/FSCalendar/include/module.modulemap", "-fmodule-map-file=$(SRCROOT)/../../Tuist/.build/tuist-derived/RxCocoaRuntime/RxCocoaRuntime.modulemap", ); OTHER_SWIFT_FLAGS = ( "$(inherited)", "-Xcc", + "-fmodule-map-file=$(SRCROOT)/../../Tuist/.build/checkouts/FSCalendar/FSCalendar/include/module.modulemap", + "-Xcc", "-fmodule-map-file=$(SRCROOT)/../../Tuist/.build/tuist-derived/RxCocoaRuntime/RxCocoaRuntime.modulemap", ); PRODUCT_BUNDLE_IDENTIFIER = io.tuist.HomeTests; @@ -495,7 +507,7 @@ SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; SWIFT_COMPILATION_MODE = wholemodule; - SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; + SWIFT_OPTIMIZATION_LEVEL = "-O"; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; }; @@ -507,6 +519,7 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; HEADER_SEARCH_PATHS = ( "$(inherited)", + "$(SRCROOT)/../../Tuist/.build/checkouts/FSCalendar/FSCalendar/include", "$(SRCROOT)/../../Tuist/.build/checkouts/RxSwift/Sources/RxCocoaRuntime/include", ); INFOPLIST_FILE = "Derived/InfoPlists/HomeTests-Info.plist"; @@ -517,11 +530,14 @@ ); OTHER_CFLAGS = ( "$(inherited)", + "-fmodule-map-file=$(SRCROOT)/../../Tuist/.build/checkouts/FSCalendar/FSCalendar/include/module.modulemap", "-fmodule-map-file=$(SRCROOT)/../../Tuist/.build/tuist-derived/RxCocoaRuntime/RxCocoaRuntime.modulemap", ); OTHER_SWIFT_FLAGS = ( "$(inherited)", "-Xcc", + "-fmodule-map-file=$(SRCROOT)/../../Tuist/.build/checkouts/FSCalendar/FSCalendar/include/module.modulemap", + "-Xcc", "-fmodule-map-file=$(SRCROOT)/../../Tuist/.build/tuist-derived/RxCocoaRuntime/RxCocoaRuntime.modulemap", ); PRODUCT_BUNDLE_IDENTIFIER = io.tuist.HomeTests; diff --git a/Projects/LearnMate/LearnMate.xcodeproj/project.pbxproj b/Projects/LearnMate/LearnMate.xcodeproj/project.pbxproj index b7ec6c9..5b71397 100644 --- a/Projects/LearnMate/LearnMate.xcodeproj/project.pbxproj +++ b/Projects/LearnMate/LearnMate.xcodeproj/project.pbxproj @@ -109,7 +109,6 @@ 7C422FE2DDF9127C2F4B2DF0 /* RxSwift.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = RxSwift.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 89F58ED76FC4C0973305766F /* DependencyInjector.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DependencyInjector.swift; sourceTree = ""; }; 8A00D2BDD6B9A0B6E62CF3CC /* LearnMateTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = LearnMateTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; - 950A0D4D2E56D23300C07CF2 /* LearnMate.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; name = LearnMate.entitlements; path = LearnMate/LearnMate.entitlements; sourceTree = ""; }; 9ACFF0B3105009DD7646173E /* Domain.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Domain.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 9AEA88AC00680DC27F375FB6 /* HomeAssembly.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeAssembly.swift; sourceTree = ""; }; A4DC536F09A4A8284AA20CE7 /* CommonUI.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = CommonUI.framework; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -199,7 +198,6 @@ E290CD2FA6D313265F045E73 = { isa = PBXGroup; children = ( - 950A0D4D2E56D23300C07CF2 /* LearnMate.entitlements */, B074F9F8E730729626040CA5 /* Project */, ED0EE4EA4327EC4E4D07675E /* Products */, ); @@ -231,13 +229,13 @@ FF553F19160719C6C483E8C6 /* DI */ = { isa = PBXGroup; children = ( - F39319B32F0C0DD673EA6D68 /* ChatAssembly.swift */, - 19B287BCC12CA22DAF2F8DE5 /* DataAssembly.swift */, 89F58ED76FC4C0973305766F /* DependencyInjector.swift */, - 1D786F720EBD14634E77E3C1 /* DiaryAssembly.swift */, + 19B287BCC12CA22DAF2F8DE5 /* DataAssembly.swift */, EEAA29E105D3AB0BAD502401 /* DomainAssembly.swift */, - 9AEA88AC00680DC27F375FB6 /* HomeAssembly.swift */, B097E64BFDAE1FCC4E0AB423 /* LoginAssembly.swift */, + 9AEA88AC00680DC27F375FB6 /* HomeAssembly.swift */, + F39319B32F0C0DD673EA6D68 /* ChatAssembly.swift */, + 1D786F720EBD14634E77E3C1 /* DiaryAssembly.swift */, C25294FAE7464C63791082A4 /* MyPageAssembly.swift */, ); path = DI; @@ -380,11 +378,11 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; - CODE_SIGN_ENTITLEMENTS = LearnMate/LearnMate.entitlements; CODE_SIGN_IDENTITY = "iPhone Developer"; ENABLE_PREVIEWS = YES; HEADER_SEARCH_PATHS = ( "$(inherited)", + "$(SRCROOT)/../../Tuist/.build/checkouts/FSCalendar/FSCalendar/include", "$(SRCROOT)/../../Tuist/.build/checkouts/RxSwift/Sources/RxCocoaRuntime/include", ); INFOPLIST_FILE = "Support/LearnMate-Info.plist"; @@ -394,9 +392,10 @@ ); OTHER_CFLAGS = ( "$(inherited)", + "-fmodule-map-file=$(SRCROOT)/../../Tuist/.build/checkouts/FSCalendar/FSCalendar/include/module.modulemap", "-fmodule-map-file=$(SRCROOT)/../../Tuist/.build/tuist-derived/RxCocoaRuntime/RxCocoaRuntime.modulemap", ); - OTHER_SWIFT_FLAGS = "$(inherited) -Xcc -fmodule-map-file=$(SRCROOT)/../../Tuist/.build/tuist-derived/RxCocoaRuntime/RxCocoaRuntime.modulemap"; + OTHER_SWIFT_FLAGS = "$(inherited) -Xcc -fmodule-map-file=$(SRCROOT)/../../Tuist/.build/checkouts/FSCalendar/FSCalendar/include/module.modulemap -Xcc -fmodule-map-file=$(SRCROOT)/../../Tuist/.build/tuist-derived/RxCocoaRuntime/RxCocoaRuntime.modulemap"; PRODUCT_BUNDLE_IDENTIFIER = io.tuist.LearnMate; PRODUCT_NAME = LearnMate; SDKROOT = iphoneos; @@ -404,7 +403,7 @@ SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; SWIFT_COMPILATION_MODE = wholemodule; - SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; + SWIFT_OPTIMIZATION_LEVEL = "-O"; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; }; @@ -415,11 +414,11 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; - CODE_SIGN_ENTITLEMENTS = LearnMate/LearnMate.entitlements; CODE_SIGN_IDENTITY = "iPhone Developer"; ENABLE_PREVIEWS = YES; HEADER_SEARCH_PATHS = ( "$(inherited)", + "$(SRCROOT)/../../Tuist/.build/checkouts/FSCalendar/FSCalendar/include", "$(SRCROOT)/../../Tuist/.build/checkouts/RxSwift/Sources/RxCocoaRuntime/include", ); INFOPLIST_FILE = "Support/LearnMate-Info.plist"; @@ -429,9 +428,10 @@ ); OTHER_CFLAGS = ( "$(inherited)", + "-fmodule-map-file=$(SRCROOT)/../../Tuist/.build/checkouts/FSCalendar/FSCalendar/include/module.modulemap", "-fmodule-map-file=$(SRCROOT)/../../Tuist/.build/tuist-derived/RxCocoaRuntime/RxCocoaRuntime.modulemap", ); - OTHER_SWIFT_FLAGS = "$(inherited) -Xcc -fmodule-map-file=$(SRCROOT)/../../Tuist/.build/tuist-derived/RxCocoaRuntime/RxCocoaRuntime.modulemap"; + OTHER_SWIFT_FLAGS = "$(inherited) -Xcc -fmodule-map-file=$(SRCROOT)/../../Tuist/.build/checkouts/FSCalendar/FSCalendar/include/module.modulemap -Xcc -fmodule-map-file=$(SRCROOT)/../../Tuist/.build/tuist-derived/RxCocoaRuntime/RxCocoaRuntime.modulemap"; PRODUCT_BUNDLE_IDENTIFIER = io.tuist.LearnMate; PRODUCT_NAME = LearnMate; SDKROOT = iphoneos; @@ -512,6 +512,7 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; HEADER_SEARCH_PATHS = ( "$(inherited)", + "$(SRCROOT)/../../Tuist/.build/checkouts/FSCalendar/FSCalendar/include", "$(SRCROOT)/../../Tuist/.build/checkouts/RxSwift/Sources/RxCocoaRuntime/include", ); INFOPLIST_FILE = "Support/LearnMateTests-Info.plist"; @@ -522,9 +523,10 @@ ); OTHER_CFLAGS = ( "$(inherited)", + "-fmodule-map-file=$(SRCROOT)/../../Tuist/.build/checkouts/FSCalendar/FSCalendar/include/module.modulemap", "-fmodule-map-file=$(SRCROOT)/../../Tuist/.build/tuist-derived/RxCocoaRuntime/RxCocoaRuntime.modulemap", ); - OTHER_SWIFT_FLAGS = "$(inherited) -Xcc -fmodule-map-file=$(SRCROOT)/../../Tuist/.build/tuist-derived/RxCocoaRuntime/RxCocoaRuntime.modulemap"; + OTHER_SWIFT_FLAGS = "$(inherited) -Xcc -fmodule-map-file=$(SRCROOT)/../../Tuist/.build/checkouts/FSCalendar/FSCalendar/include/module.modulemap -Xcc -fmodule-map-file=$(SRCROOT)/../../Tuist/.build/tuist-derived/RxCocoaRuntime/RxCocoaRuntime.modulemap"; PRODUCT_BUNDLE_IDENTIFIER = io.tuist.LearnMateTests; PRODUCT_NAME = LearnMateTests; SDKROOT = iphoneos; @@ -548,6 +550,7 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; HEADER_SEARCH_PATHS = ( "$(inherited)", + "$(SRCROOT)/../../Tuist/.build/checkouts/FSCalendar/FSCalendar/include", "$(SRCROOT)/../../Tuist/.build/checkouts/RxSwift/Sources/RxCocoaRuntime/include", ); INFOPLIST_FILE = "Support/LearnMateTests-Info.plist"; @@ -558,9 +561,10 @@ ); OTHER_CFLAGS = ( "$(inherited)", + "-fmodule-map-file=$(SRCROOT)/../../Tuist/.build/checkouts/FSCalendar/FSCalendar/include/module.modulemap", "-fmodule-map-file=$(SRCROOT)/../../Tuist/.build/tuist-derived/RxCocoaRuntime/RxCocoaRuntime.modulemap", ); - OTHER_SWIFT_FLAGS = "$(inherited) -Xcc -fmodule-map-file=$(SRCROOT)/../../Tuist/.build/tuist-derived/RxCocoaRuntime/RxCocoaRuntime.modulemap"; + OTHER_SWIFT_FLAGS = "$(inherited) -Xcc -fmodule-map-file=$(SRCROOT)/../../Tuist/.build/checkouts/FSCalendar/FSCalendar/include/module.modulemap -Xcc -fmodule-map-file=$(SRCROOT)/../../Tuist/.build/tuist-derived/RxCocoaRuntime/RxCocoaRuntime.modulemap"; PRODUCT_BUNDLE_IDENTIFIER = io.tuist.LearnMateTests; PRODUCT_NAME = LearnMateTests; SDKROOT = iphoneos; @@ -568,7 +572,7 @@ SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; SWIFT_COMPILATION_MODE = wholemodule; - SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; + SWIFT_OPTIMIZATION_LEVEL = "-O"; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/LearnMate.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/LearnMate"; diff --git a/Projects/LearnMate/Sources/Application/SceneDelegate.swift b/Projects/LearnMate/Sources/Application/SceneDelegate.swift index 8beab04..a4fc7bf 100644 --- a/Projects/LearnMate/Sources/Application/SceneDelegate.swift +++ b/Projects/LearnMate/Sources/Application/SceneDelegate.swift @@ -33,7 +33,9 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate { DomainAssembly(), LoginAssembly(), HomeAssembly(), - ChatAssembly()]) + ChatAssembly(), + DiaryAssembly(), + MyPageAssembly()]) appCoordinator?.start() } diff --git a/Projects/LearnMate/Sources/Coordinator/AppCoordinator.swift b/Projects/LearnMate/Sources/Coordinator/AppCoordinator.swift index 1e04d7c..d1bef94 100644 --- a/Projects/LearnMate/Sources/Coordinator/AppCoordinator.swift +++ b/Projects/LearnMate/Sources/Coordinator/AppCoordinator.swift @@ -9,6 +9,8 @@ import CommonUI import Home import UIKit import Login +import RxSwift +import Domain protocol AppCoordinator: Coordinator { // func showLoginFlow() @@ -25,6 +27,7 @@ final class DefaultAppCoordinator: AppCoordinator{ } private let dependency: Dependency + private let disposeBag = DisposeBag() var childCoordinators: [Coordinator] = [] var navigationController: UINavigationController var type: CoordinatorType = .app @@ -36,6 +39,50 @@ final class DefaultAppCoordinator: AppCoordinator{ } func start() { + // 자동 로그인 확인 + checkAutoLogin() + } + + private func checkAutoLogin() { + let tokenRepository = dependency.injector.resolve(TokenRepository.self) + + // 먼저 토큰이 있는지 빠르게 확인 + guard let accessToken = tokenRepository.getAccessToken(), !accessToken.isEmpty else { + print("❌ 저장된 토큰 없음, 로그인 화면으로 이동") + showLoginFlow() + return + } + + print("🔑 저장된 토큰 발견, 토큰 검증 시작") + + let tokenValidationUseCase = dependency.injector.resolve(TokenValidationUseCase.self) + + tokenValidationUseCase.validateToken() + .subscribe(onSuccess: { [weak self] isValid in + guard let self = self else { return } + + if isValid { + print("✅ 토큰 검증 성공, 자동 로그인 완료") + DispatchQueue.main.async { + self.showTabbarFlow() + } + } else { + print("❌ 토큰 무효, 로그인 화면으로 이동") + DispatchQueue.main.async { + self.showLoginFlow() + } + } + }, onFailure: { [weak self] error in + guard let self = self else { return } + print("❌ 토큰 검증 네트워크 에러: \(error), 로그인 화면으로 이동") + DispatchQueue.main.async { + self.showLoginFlow() + } + }) + .disposed(by: disposeBag) + } + + private func showLoginFlow() { let loginViewController = dependency.injector.resolve(LoginViewController.self) loginViewController.onPresentLmLogin = { [weak self] in guard let self else { return } @@ -52,13 +99,13 @@ final class DefaultAppCoordinator: AppCoordinator{ self.navigationController.pushViewController(signInViewController, animated: true) } self.navigationController.pushViewController(loginViewController, animated: true) -// setNavigationBar() -// setTabBarCoordinator() -// showTabbarFlow() } /// 탭바 컨트롤러 플로우 func showTabbarFlow() { + // 네비게이션 바 숨기기 + navigationController.setNavigationBarHidden(true, animated: false) + if getChildCoordinator(.tabbar) == nil { setTabBarCoordinator() } let tabBarCoordinator = getChildCoordinator(.tabbar) as! TabBarCoordinator tabBarCoordinator.start() @@ -101,6 +148,11 @@ final class DefaultAppCoordinator: AppCoordinator{ /// 자식 코디네이터가 종료되었을 때 실행할 메서드 extension DefaultAppCoordinator: CoordinatorFinishDelegate { func coordinatorDidFinish(childCoordinator: Coordinator) { + childCoordinators.removeAll { $0 === childCoordinator } + // TabBarCoordinator가 종료되면 로그인 화면으로 이동 + if childCoordinator is TabBarCoordinator { + showLoginFlow() + } } } diff --git a/Projects/LearnMate/Sources/Coordinator/TabBarCoordinator.swift b/Projects/LearnMate/Sources/Coordinator/TabBarCoordinator.swift index 7c7d334..2160fbd 100644 --- a/Projects/LearnMate/Sources/Coordinator/TabBarCoordinator.swift +++ b/Projects/LearnMate/Sources/Coordinator/TabBarCoordinator.swift @@ -10,6 +10,8 @@ import Login import Home import UIKit import Chat +import Diary +import MyPage protocol TabBarCoordinator: Coordinator { var tabBarController: UITabBarController { get } @@ -46,7 +48,10 @@ final class DefaultTabBarController: TabBarCoordinator { /// 각 탭바에 들어갈 네비게이션 컨트롤러 생성 private func createTabNavigationController(of page: TabBarPage) -> UINavigationController { let tabNavigationController = UINavigationController() - tabNavigationController.setNavigationBarHidden(false, animated: false) + + // 모든 탭에서 네비게이션 바 숨기기 + tabNavigationController.setNavigationBarHidden(true, animated: false) + tabNavigationController.tabBarItem = self.configureTabBarItem(of: page) self.startTabCoordinator(of: page, to: tabNavigationController) return tabNavigationController @@ -97,12 +102,27 @@ final class DefaultTabBarController: TabBarCoordinator { case .chat: let chatMainViewController = dependency.injector.resolve(ChatMainViewController.self) tabNavigationController.pushViewController(chatMainViewController, animated: true) + case .diary: + let diaryViewController = dependency.injector.resolve(DiaryViewController.self) + tabNavigationController.pushViewController(diaryViewController, animated: true) + case .myPage: + let myPageViewController = dependency.injector.resolve(MyPageViewController.self) + myPageViewController.onLogout = { [weak self] in + self?.handleLogout() + } + tabNavigationController.pushViewController(myPageViewController, animated: true) default: let viewController = UIViewController() viewController.view.backgroundColor = .black tabNavigationController.pushViewController(viewController, animated: true) } } + + private func handleLogout() { + // 모든 뷰 컨트롤러 제거하고 로그인 화면으로 이동 + navigationController.viewControllers.removeAll() + finishDelegate?.coordinatorDidFinish(childCoordinator: self) + } } enum TabBarPage: String, CaseIterable { diff --git a/Projects/LearnMate/Sources/DI/DataAssembly.swift b/Projects/LearnMate/Sources/DI/DataAssembly.swift index ace87bc..aa3abf7 100644 --- a/Projects/LearnMate/Sources/DI/DataAssembly.swift +++ b/Projects/LearnMate/Sources/DI/DataAssembly.swift @@ -37,5 +37,10 @@ public struct DataAssembly: Assembly { let tokenRepository = resolver.resolve(TokenRepository.self)! return DefaultChatRepository(tokenRepository: tokenRepository) } + + container.register(DiaryRepository.self) { resolver in + let tokenRepository = resolver.resolve(TokenRepository.self)! + return DefaultDiaryRepository(tokenRepository: tokenRepository) + } } } diff --git a/Projects/LearnMate/Sources/DI/DiaryAssembly.swift b/Projects/LearnMate/Sources/DI/DiaryAssembly.swift index 48db243..5e35930 100644 --- a/Projects/LearnMate/Sources/DI/DiaryAssembly.swift +++ b/Projects/LearnMate/Sources/DI/DiaryAssembly.swift @@ -5,3 +5,22 @@ // Created by 박지윤 on 7/2/25. // +import Swinject +import Diary +import Domain + +public struct DiaryAssembly: Assembly { + public func assemble(container: Container) { + container.register(DiaryViewModel.self) { resolver in + let diaryUseCase = resolver.resolve(DiaryUseCase.self)! + let tokenUseCase = resolver.resolve(TokenUseCase.self)! + return DiaryViewModel(diaryUseCase: diaryUseCase, + tokenUseCase: tokenUseCase) + } + + container.register(DiaryViewController.self) { resolver in + let diaryViewModel = resolver.resolve(DiaryViewModel.self)! + return DiaryViewController(diaryViewModel: diaryViewModel) + } + } +} diff --git a/Projects/LearnMate/Sources/DI/DomainAssembly.swift b/Projects/LearnMate/Sources/DI/DomainAssembly.swift index 38f4ffa..d20bc18 100644 --- a/Projects/LearnMate/Sources/DI/DomainAssembly.swift +++ b/Projects/LearnMate/Sources/DI/DomainAssembly.swift @@ -25,6 +25,11 @@ public struct DomainAssembly: Assembly { return DefaultTokenUseCase(repository: repository) } + container.register(TokenValidationUseCase.self) { resolver in + let repository = resolver.resolve(TokenRepository.self)! + return DefaultTokenValidationUseCase(repository: repository) + } + container.register(QuizUseCase.self) { resolver in let repository = resolver.resolve(QuizRepository.self)! return DefaultQuizUseCase(repository: repository) @@ -39,5 +44,10 @@ public struct DomainAssembly: Assembly { let repository = resolver.resolve(ChatRepository.self)! return DefaultChatUseCase(repository: repository) } + + container.register(DiaryUseCase.self) { resolver in + let repository = resolver.resolve(DiaryRepository.self)! + return DefaultDiaryUseCase(repository: repository) + } } } diff --git a/Projects/LearnMate/Sources/DI/MyPageAssembly.swift b/Projects/LearnMate/Sources/DI/MyPageAssembly.swift index 540aa51..22e6dc3 100644 --- a/Projects/LearnMate/Sources/DI/MyPageAssembly.swift +++ b/Projects/LearnMate/Sources/DI/MyPageAssembly.swift @@ -2,5 +2,23 @@ // MyPageAssembly.swift // LearnMate // -// Created by 박지윤 on 7/2/25. +// Created by 박지윤 on 1/7/25. // + +import MyPage +import Domain +import Swinject + +public struct MyPageAssembly: Assembly { + public func assemble(container: Container) { + container.register(MyPageViewModel.self) { resolver in + let tokenUseCase = resolver.resolve(TokenUseCase.self)! + return MyPageViewModel(tokenUseCase: tokenUseCase) + } + + container.register(MyPageViewController.self) { resolver in + let viewModel = resolver.resolve(MyPageViewModel.self)! + return MyPageViewController(myPageViewModel: viewModel) + } + } +} \ No newline at end of file diff --git a/Projects/Login/Login.xcodeproj/project.pbxproj b/Projects/Login/Login.xcodeproj/project.pbxproj index 15f0774..0f5a99d 100644 --- a/Projects/Login/Login.xcodeproj/project.pbxproj +++ b/Projects/Login/Login.xcodeproj/project.pbxproj @@ -3,23 +3,23 @@ archiveVersion = 1; classes = { }; - objectVersion = 56; + objectVersion = 55; objects = { /* Begin PBXBuildFile section */ + 1C2FF4EC99DC8AFDA6474A54 /* SignUpViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4530CC29B42C645D90C6417F /* SignUpViewController.swift */; }; 250A492BA0E6132DCEBD7CE7 /* Then.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9A9C214589834256AC9DD6AB /* Then.framework */; }; 4027F4EA82D89F9520C87D34 /* LoginViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 159850B93C915DFB2D697BF2 /* LoginViewController.swift */; }; + 47A5B19D727E094AB3E609C2 /* SignUpCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4D94347C029993F3A7163E0D /* SignUpCoordinator.swift */; }; 58DB5E0A3D1E600D0B90C66D /* Alamofire.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 7D44C290B1A433AEF98F1870 /* Alamofire.framework */; }; 6D45A5743268B6AB834A00C0 /* Domain.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6CF3B7E565CEC77F3F96CE8E /* Domain.framework */; }; 92D7E2A0C6D4E3A6F57BAFB0 /* LoginViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = C32E71FB3926A3A3118D0096 /* LoginViewModel.swift */; }; - 950A0D5E2E5C3A6D00C07CF2 /* SignInViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 950A0D5D2E5C3A6700C07CF2 /* SignInViewController.swift */; }; - 950A0D642E5C5F7D00C07CF2 /* SignUpViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 950A0D632E5C5F7900C07CF2 /* SignUpViewController.swift */; }; - 950A0D662E5CCB2F00C07CF2 /* SignViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 950A0D652E5CCB2900C07CF2 /* SignViewModel.swift */; }; - 950A0D862E5DE6D000C07CF2 /* SignInCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 950A0D852E5DE6C600C07CF2 /* SignInCoordinator.swift */; }; - 950A0D8A2E5E0C8600C07CF2 /* SignUpCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 950A0D892E5E0C8000C07CF2 /* SignUpCoordinator.swift */; }; 9543B5BD011752760C9E8D48 /* Common.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5A3E03A11BBC654DEC110ECE /* Common.framework */; }; + 9CA5834F18EE0313B5DDD396 /* SignInViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = AFCD60F685267E861FDA1FC6 /* SignInViewController.swift */; }; A2DD99D036B33B4B67E19670 /* RxSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 149968626029AF0A6483A522 /* RxSwift.framework */; }; A60EB3815F8DEA7C7B2A1E4D /* SnapKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A78DFB39E6F2ADF7C38CFA79 /* SnapKit.framework */; }; + B12493E0ECAB257C106608C4 /* SignViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 845C804405C71F85DA84779A /* SignViewModel.swift */; }; + D28E2FC4AE5F7ACDE801E66B /* SignInCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = B7F05C3948307A51BA730E85 /* SignInCoordinator.swift */; }; E603A7E7971D4E54E1F59146 /* Login.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 28C6E6787DFAF624F53D6F5B /* Login.framework */; }; F841FF334E1C6034EABAEDCB /* CommonUI.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 21D6E91FA13D3D7D7989E850 /* CommonUI.framework */; }; /* End PBXBuildFile section */ @@ -63,18 +63,18 @@ 1F122292170A9B08F370E93A /* Login-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = "Login-Info.plist"; sourceTree = ""; }; 21D6E91FA13D3D7D7989E850 /* CommonUI.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = CommonUI.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 28C6E6787DFAF624F53D6F5B /* Login.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Login.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 4530CC29B42C645D90C6417F /* SignUpViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SignUpViewController.swift; sourceTree = ""; }; + 4D94347C029993F3A7163E0D /* SignUpCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SignUpCoordinator.swift; sourceTree = ""; }; 5A3E03A11BBC654DEC110ECE /* Common.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Common.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 6CF3B7E565CEC77F3F96CE8E /* Domain.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Domain.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 72D21628961E74D99945474E /* LoginTests-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = "LoginTests-Info.plist"; sourceTree = ""; }; 7D44C290B1A433AEF98F1870 /* Alamofire.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Alamofire.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 950A0D5D2E5C3A6700C07CF2 /* SignInViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SignInViewController.swift; sourceTree = ""; }; - 950A0D632E5C5F7900C07CF2 /* SignUpViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SignUpViewController.swift; sourceTree = ""; }; - 950A0D652E5CCB2900C07CF2 /* SignViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SignViewModel.swift; sourceTree = ""; }; - 950A0D852E5DE6C600C07CF2 /* SignInCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SignInCoordinator.swift; sourceTree = ""; }; - 950A0D892E5E0C8000C07CF2 /* SignUpCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SignUpCoordinator.swift; sourceTree = ""; }; + 845C804405C71F85DA84779A /* SignViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SignViewModel.swift; sourceTree = ""; }; 9A9C214589834256AC9DD6AB /* Then.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Then.framework; sourceTree = BUILT_PRODUCTS_DIR; }; A6D197429DF4204F655968BA /* LoginTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = LoginTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; A78DFB39E6F2ADF7C38CFA79 /* SnapKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SnapKit.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + AFCD60F685267E861FDA1FC6 /* SignInViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SignInViewController.swift; sourceTree = ""; }; + B7F05C3948307A51BA730E85 /* SignInCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SignInCoordinator.swift; sourceTree = ""; }; C32E71FB3926A3A3118D0096 /* LoginViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoginViewModel.swift; sourceTree = ""; }; /* End PBXFileReference section */ @@ -108,8 +108,8 @@ isa = PBXGroup; children = ( 159850B93C915DFB2D697BF2 /* LoginViewController.swift */, - 950A0D5D2E5C3A6700C07CF2 /* SignInViewController.swift */, - 950A0D632E5C5F7900C07CF2 /* SignUpViewController.swift */, + AFCD60F685267E861FDA1FC6 /* SignInViewController.swift */, + 4530CC29B42C645D90C6417F /* SignUpViewController.swift */, ); path = View; sourceTree = ""; @@ -151,7 +151,7 @@ isa = PBXGroup; children = ( C32E71FB3926A3A3118D0096 /* LoginViewModel.swift */, - 950A0D652E5CCB2900C07CF2 /* SignViewModel.swift */, + 845C804405C71F85DA84779A /* SignViewModel.swift */, ); path = ViewModel; sourceTree = ""; @@ -159,18 +159,18 @@ 82158BAC7310E834F5971A06 /* Sources */ = { isa = PBXGroup; children = ( - 950A0D842E5DE4F000C07CF2 /* Coordinator */, + 96E8BAEDF1C0DEE2376290F2 /* Coordinator */, 04AE692FD1ACBC62C53BE327 /* View */, 787BEC2C2BCE59B03D483875 /* ViewModel */, ); path = Sources; sourceTree = ""; }; - 950A0D842E5DE4F000C07CF2 /* Coordinator */ = { + 96E8BAEDF1C0DEE2376290F2 /* Coordinator */ = { isa = PBXGroup; children = ( - 950A0D852E5DE6C600C07CF2 /* SignInCoordinator.swift */, - 950A0D892E5E0C8000C07CF2 /* SignUpCoordinator.swift */, + B7F05C3948307A51BA730E85 /* SignInCoordinator.swift */, + 4D94347C029993F3A7163E0D /* SignUpCoordinator.swift */, ); path = Coordinator; sourceTree = ""; @@ -243,6 +243,8 @@ isa = PBXProject; attributes = { BuildIndependentTargetsInParallel = YES; + TargetAttributes = { + }; }; buildConfigurationList = 6C10079AF94881EFE73379E4 /* Build configuration list for PBXProject "Login" */; compatibilityVersion = "Xcode 14.0"; @@ -285,13 +287,13 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + D28E2FC4AE5F7ACDE801E66B /* SignInCoordinator.swift in Sources */, + 47A5B19D727E094AB3E609C2 /* SignUpCoordinator.swift in Sources */, 4027F4EA82D89F9520C87D34 /* LoginViewController.swift in Sources */, - 950A0D8A2E5E0C8600C07CF2 /* SignUpCoordinator.swift in Sources */, + 9CA5834F18EE0313B5DDD396 /* SignInViewController.swift in Sources */, + 1C2FF4EC99DC8AFDA6474A54 /* SignUpViewController.swift in Sources */, 92D7E2A0C6D4E3A6F57BAFB0 /* LoginViewModel.swift in Sources */, - 950A0D662E5CCB2F00C07CF2 /* SignViewModel.swift in Sources */, - 950A0D642E5C5F7D00C07CF2 /* SignUpViewController.swift in Sources */, - 950A0D862E5DE6D000C07CF2 /* SignInCoordinator.swift in Sources */, - 950A0D5E2E5C3A6D00C07CF2 /* SignInViewController.swift in Sources */, + B12493E0ECAB257C106608C4 /* SignViewModel.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -320,6 +322,7 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; HEADER_SEARCH_PATHS = ( "$(inherited)", + "$(SRCROOT)/../../Tuist/.build/checkouts/FSCalendar/FSCalendar/include", "$(SRCROOT)/../../Tuist/.build/checkouts/RxSwift/Sources/RxCocoaRuntime/include", ); INFOPLIST_FILE = "Derived/InfoPlists/LoginTests-Info.plist"; @@ -330,16 +333,26 @@ ); OTHER_CFLAGS = ( "$(inherited)", + "-fmodule-map-file=$(SRCROOT)/../../Tuist/.build/checkouts/FSCalendar/FSCalendar/include/module.modulemap", + "-fmodule-map-file=$(SRCROOT)/../../Tuist/.build/tuist-derived/RxCocoaRuntime/RxCocoaRuntime.modulemap", + ); + OTHER_SWIFT_FLAGS = ( + "$(inherited)", + "-Xcc", + "-fmodule-map-file=$(SRCROOT)/../../Tuist/.build/checkouts/FSCalendar/FSCalendar/include/module.modulemap", + "-Xcc", "-fmodule-map-file=$(SRCROOT)/../../Tuist/.build/tuist-derived/RxCocoaRuntime/RxCocoaRuntime.modulemap", ); - OTHER_SWIFT_FLAGS = "$(inherited) -Xcc -fmodule-map-file=$(SRCROOT)/../../Tuist/.build/tuist-derived/RxCocoaRuntime/RxCocoaRuntime.modulemap"; PRODUCT_BUNDLE_IDENTIFIER = io.tuist.LoginTests; PRODUCT_NAME = LoginTests; SDKROOT = iphoneos; SUPPORTS_MACCATALYST = NO; SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; - SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) DEBUG"; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = ( + "$(inherited)", + DEBUG, + ); SWIFT_COMPILATION_MODE = singlefile; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 5.0; @@ -353,6 +366,7 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; HEADER_SEARCH_PATHS = ( "$(inherited)", + "$(SRCROOT)/../../Tuist/.build/checkouts/FSCalendar/FSCalendar/include", "$(SRCROOT)/../../Tuist/.build/checkouts/RxSwift/Sources/RxCocoaRuntime/include", ); INFOPLIST_FILE = "Derived/InfoPlists/LoginTests-Info.plist"; @@ -363,9 +377,16 @@ ); OTHER_CFLAGS = ( "$(inherited)", + "-fmodule-map-file=$(SRCROOT)/../../Tuist/.build/checkouts/FSCalendar/FSCalendar/include/module.modulemap", + "-fmodule-map-file=$(SRCROOT)/../../Tuist/.build/tuist-derived/RxCocoaRuntime/RxCocoaRuntime.modulemap", + ); + OTHER_SWIFT_FLAGS = ( + "$(inherited)", + "-Xcc", + "-fmodule-map-file=$(SRCROOT)/../../Tuist/.build/checkouts/FSCalendar/FSCalendar/include/module.modulemap", + "-Xcc", "-fmodule-map-file=$(SRCROOT)/../../Tuist/.build/tuist-derived/RxCocoaRuntime/RxCocoaRuntime.modulemap", ); - OTHER_SWIFT_FLAGS = "$(inherited) -Xcc -fmodule-map-file=$(SRCROOT)/../../Tuist/.build/tuist-derived/RxCocoaRuntime/RxCocoaRuntime.modulemap"; PRODUCT_BUNDLE_IDENTIFIER = io.tuist.LoginTests; PRODUCT_NAME = LoginTests; SDKROOT = iphoneos; @@ -373,7 +394,7 @@ SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; SWIFT_COMPILATION_MODE = wholemodule; - SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; + SWIFT_OPTIMIZATION_LEVEL = "-O"; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; }; @@ -390,6 +411,7 @@ DYLIB_INSTALL_NAME_BASE = "@rpath"; HEADER_SEARCH_PATHS = ( "$(inherited)", + "$(SRCROOT)/../../Tuist/.build/checkouts/FSCalendar/FSCalendar/include", "$(SRCROOT)/../../Tuist/.build/checkouts/RxSwift/Sources/RxCocoaRuntime/include", ); INFOPLIST_FILE = "Derived/InfoPlists/Login-Info.plist"; @@ -401,9 +423,16 @@ ); OTHER_CFLAGS = ( "$(inherited)", + "-fmodule-map-file=$(SRCROOT)/../../Tuist/.build/checkouts/FSCalendar/FSCalendar/include/module.modulemap", + "-fmodule-map-file=$(SRCROOT)/../../Tuist/.build/tuist-derived/RxCocoaRuntime/RxCocoaRuntime.modulemap", + ); + OTHER_SWIFT_FLAGS = ( + "$(inherited)", + "-Xcc", + "-fmodule-map-file=$(SRCROOT)/../../Tuist/.build/checkouts/FSCalendar/FSCalendar/include/module.modulemap", + "-Xcc", "-fmodule-map-file=$(SRCROOT)/../../Tuist/.build/tuist-derived/RxCocoaRuntime/RxCocoaRuntime.modulemap", ); - OTHER_SWIFT_FLAGS = "$(inherited) -Xcc -fmodule-map-file=$(SRCROOT)/../../Tuist/.build/tuist-derived/RxCocoaRuntime/RxCocoaRuntime.modulemap"; PRODUCT_BUNDLE_IDENTIFIER = io.tuist.Login; PRODUCT_NAME = Login; SDKROOT = iphoneos; @@ -412,7 +441,7 @@ SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; SWIFT_COMPILATION_MODE = wholemodule; - SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; + SWIFT_OPTIMIZATION_LEVEL = "-O"; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; VERSIONING_SYSTEM = "apple-generic"; @@ -431,6 +460,7 @@ DYLIB_INSTALL_NAME_BASE = "@rpath"; HEADER_SEARCH_PATHS = ( "$(inherited)", + "$(SRCROOT)/../../Tuist/.build/checkouts/FSCalendar/FSCalendar/include", "$(SRCROOT)/../../Tuist/.build/checkouts/RxSwift/Sources/RxCocoaRuntime/include", ); INFOPLIST_FILE = "Derived/InfoPlists/Login-Info.plist"; @@ -442,9 +472,16 @@ ); OTHER_CFLAGS = ( "$(inherited)", + "-fmodule-map-file=$(SRCROOT)/../../Tuist/.build/checkouts/FSCalendar/FSCalendar/include/module.modulemap", + "-fmodule-map-file=$(SRCROOT)/../../Tuist/.build/tuist-derived/RxCocoaRuntime/RxCocoaRuntime.modulemap", + ); + OTHER_SWIFT_FLAGS = ( + "$(inherited)", + "-Xcc", + "-fmodule-map-file=$(SRCROOT)/../../Tuist/.build/checkouts/FSCalendar/FSCalendar/include/module.modulemap", + "-Xcc", "-fmodule-map-file=$(SRCROOT)/../../Tuist/.build/tuist-derived/RxCocoaRuntime/RxCocoaRuntime.modulemap", ); - OTHER_SWIFT_FLAGS = "$(inherited) -Xcc -fmodule-map-file=$(SRCROOT)/../../Tuist/.build/tuist-derived/RxCocoaRuntime/RxCocoaRuntime.modulemap"; PRODUCT_BUNDLE_IDENTIFIER = io.tuist.Login; PRODUCT_NAME = Login; SDKROOT = iphoneos; @@ -452,7 +489,10 @@ SUPPORTS_MACCATALYST = NO; SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; - SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) DEBUG"; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = ( + "$(inherited)", + DEBUG, + ); SWIFT_COMPILATION_MODE = singlefile; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 5.0; diff --git a/Projects/MyPage/MyPage.xcodeproj/project.pbxproj b/Projects/MyPage/MyPage.xcodeproj/project.pbxproj index 1a6d36b..f77576a 100644 --- a/Projects/MyPage/MyPage.xcodeproj/project.pbxproj +++ b/Projects/MyPage/MyPage.xcodeproj/project.pbxproj @@ -319,6 +319,7 @@ DYLIB_INSTALL_NAME_BASE = "@rpath"; HEADER_SEARCH_PATHS = ( "$(inherited)", + "$(SRCROOT)/../../Tuist/.build/checkouts/FSCalendar/FSCalendar/include", "$(SRCROOT)/../../Tuist/.build/checkouts/RxSwift/Sources/RxCocoaRuntime/include", ); INFOPLIST_FILE = "Derived/InfoPlists/MyPage-Info.plist"; @@ -330,11 +331,14 @@ ); OTHER_CFLAGS = ( "$(inherited)", + "-fmodule-map-file=$(SRCROOT)/../../Tuist/.build/checkouts/FSCalendar/FSCalendar/include/module.modulemap", "-fmodule-map-file=$(SRCROOT)/../../Tuist/.build/tuist-derived/RxCocoaRuntime/RxCocoaRuntime.modulemap", ); OTHER_SWIFT_FLAGS = ( "$(inherited)", "-Xcc", + "-fmodule-map-file=$(SRCROOT)/../../Tuist/.build/checkouts/FSCalendar/FSCalendar/include/module.modulemap", + "-Xcc", "-fmodule-map-file=$(SRCROOT)/../../Tuist/.build/tuist-derived/RxCocoaRuntime/RxCocoaRuntime.modulemap", ); PRODUCT_BUNDLE_IDENTIFIER = io.tuist.MyPage; @@ -421,6 +425,7 @@ DYLIB_INSTALL_NAME_BASE = "@rpath"; HEADER_SEARCH_PATHS = ( "$(inherited)", + "$(SRCROOT)/../../Tuist/.build/checkouts/FSCalendar/FSCalendar/include", "$(SRCROOT)/../../Tuist/.build/checkouts/RxSwift/Sources/RxCocoaRuntime/include", ); INFOPLIST_FILE = "Derived/InfoPlists/MyPage-Info.plist"; @@ -432,11 +437,14 @@ ); OTHER_CFLAGS = ( "$(inherited)", + "-fmodule-map-file=$(SRCROOT)/../../Tuist/.build/checkouts/FSCalendar/FSCalendar/include/module.modulemap", "-fmodule-map-file=$(SRCROOT)/../../Tuist/.build/tuist-derived/RxCocoaRuntime/RxCocoaRuntime.modulemap", ); OTHER_SWIFT_FLAGS = ( "$(inherited)", "-Xcc", + "-fmodule-map-file=$(SRCROOT)/../../Tuist/.build/checkouts/FSCalendar/FSCalendar/include/module.modulemap", + "-Xcc", "-fmodule-map-file=$(SRCROOT)/../../Tuist/.build/tuist-derived/RxCocoaRuntime/RxCocoaRuntime.modulemap", ); PRODUCT_BUNDLE_IDENTIFIER = io.tuist.MyPage; @@ -447,7 +455,7 @@ SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; SWIFT_COMPILATION_MODE = wholemodule; - SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; + SWIFT_OPTIMIZATION_LEVEL = "-O"; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; VERSIONING_SYSTEM = "apple-generic"; @@ -461,6 +469,7 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; HEADER_SEARCH_PATHS = ( "$(inherited)", + "$(SRCROOT)/../../Tuist/.build/checkouts/FSCalendar/FSCalendar/include", "$(SRCROOT)/../../Tuist/.build/checkouts/RxSwift/Sources/RxCocoaRuntime/include", ); INFOPLIST_FILE = "Derived/InfoPlists/MyPageTests-Info.plist"; @@ -471,11 +480,14 @@ ); OTHER_CFLAGS = ( "$(inherited)", + "-fmodule-map-file=$(SRCROOT)/../../Tuist/.build/checkouts/FSCalendar/FSCalendar/include/module.modulemap", "-fmodule-map-file=$(SRCROOT)/../../Tuist/.build/tuist-derived/RxCocoaRuntime/RxCocoaRuntime.modulemap", ); OTHER_SWIFT_FLAGS = ( "$(inherited)", "-Xcc", + "-fmodule-map-file=$(SRCROOT)/../../Tuist/.build/checkouts/FSCalendar/FSCalendar/include/module.modulemap", + "-Xcc", "-fmodule-map-file=$(SRCROOT)/../../Tuist/.build/tuist-derived/RxCocoaRuntime/RxCocoaRuntime.modulemap", ); PRODUCT_BUNDLE_IDENTIFIER = io.tuist.MyPageTests; @@ -485,7 +497,7 @@ SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; SWIFT_COMPILATION_MODE = wholemodule; - SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; + SWIFT_OPTIMIZATION_LEVEL = "-O"; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; }; @@ -556,6 +568,7 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; HEADER_SEARCH_PATHS = ( "$(inherited)", + "$(SRCROOT)/../../Tuist/.build/checkouts/FSCalendar/FSCalendar/include", "$(SRCROOT)/../../Tuist/.build/checkouts/RxSwift/Sources/RxCocoaRuntime/include", ); INFOPLIST_FILE = "Derived/InfoPlists/MyPageTests-Info.plist"; @@ -566,11 +579,14 @@ ); OTHER_CFLAGS = ( "$(inherited)", + "-fmodule-map-file=$(SRCROOT)/../../Tuist/.build/checkouts/FSCalendar/FSCalendar/include/module.modulemap", "-fmodule-map-file=$(SRCROOT)/../../Tuist/.build/tuist-derived/RxCocoaRuntime/RxCocoaRuntime.modulemap", ); OTHER_SWIFT_FLAGS = ( "$(inherited)", "-Xcc", + "-fmodule-map-file=$(SRCROOT)/../../Tuist/.build/checkouts/FSCalendar/FSCalendar/include/module.modulemap", + "-Xcc", "-fmodule-map-file=$(SRCROOT)/../../Tuist/.build/tuist-derived/RxCocoaRuntime/RxCocoaRuntime.modulemap", ); PRODUCT_BUNDLE_IDENTIFIER = io.tuist.MyPageTests; diff --git a/Projects/MyPage/Sources/View/MyPageViewController.swift b/Projects/MyPage/Sources/View/MyPageViewController.swift index dab3184..a905fa4 100644 --- a/Projects/MyPage/Sources/View/MyPageViewController.swift +++ b/Projects/MyPage/Sources/View/MyPageViewController.swift @@ -5,3 +5,89 @@ // Created by 박지윤 on 7/1/25. // +import CommonUI +import UIKit +import SnapKit +import RxSwift + +public class MyPageViewController: BaseViewController { + let viewModel: MyPageViewModel + + let scrollView = UIScrollView() + let contentView = UIView() + let myPageView = MyPageView() + + public var onLogout: (() -> Void)? + + public init(myPageViewModel: MyPageViewModel) { + self.viewModel = myPageViewModel + super.init() + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + public override func viewDidLoad() { + super.viewDidLoad() + bindActions() + } + + public override func setupViewProperty() { + view.backgroundColor = .systemBackground + + scrollView.do { + $0.showsVerticalScrollIndicator = false + $0.showsHorizontalScrollIndicator = false + } + } + + public override func setupHierarchy() { + view.addSubview(scrollView) + scrollView.addSubview(contentView) + contentView.addSubview(myPageView) + } + + public override func setupDelegate() { + } + + public override func setupLayout() { + scrollView.snp.makeConstraints { + $0.edges.equalToSuperview() + } + + contentView.snp.makeConstraints { + $0.edges.equalToSuperview() + $0.width.equalToSuperview() + } + + myPageView.snp.makeConstraints { + $0.edges.equalToSuperview() + } + } + + public override func setupBind() { + viewModel.onLogoutSuccess = { [weak self] in + self?.onLogout?() + } + } + + private func bindActions() { + myPageView.logoutButton.rx.tap + .bind { [weak self] in + self?.showLogoutAlert() + } + .disposed(by: disposeBag) + } + + private func showLogoutAlert() { + let alert = UIAlertController(title: "로그아웃", message: "정말 로그아웃 하시겠습니까?", preferredStyle: .alert) + + alert.addAction(UIAlertAction(title: "취소", style: .cancel)) + alert.addAction(UIAlertAction(title: "로그아웃", style: .destructive) { [weak self] _ in + self?.viewModel.logout() + }) + + present(alert, animated: true) + } +} \ No newline at end of file diff --git a/Projects/MyPage/Sources/ViewModel/MyPageViewModel.swift b/Projects/MyPage/Sources/ViewModel/MyPageViewModel.swift index d19f288..db4298f 100644 --- a/Projects/MyPage/Sources/ViewModel/MyPageViewModel.swift +++ b/Projects/MyPage/Sources/ViewModel/MyPageViewModel.swift @@ -5,4 +5,23 @@ // Created by 박지윤 on 7/1/25. // +import RxSwift +import Domain +public class MyPageViewModel { + private let tokenUseCase: TokenUseCase + private let disposeBag = DisposeBag() + + public var onLogoutSuccess: (() -> Void)? + + public init(tokenUseCase: TokenUseCase) { + self.tokenUseCase = tokenUseCase + } + + public func logout() { + // 토큰 삭제 + tokenUseCase.clearAccessToken() + print("🔓 로그아웃 완료") + onLogoutSuccess?() + } +} \ No newline at end of file diff --git a/Projects/Stats/Stats.xcodeproj/project.pbxproj b/Projects/Stats/Stats.xcodeproj/project.pbxproj index 1a494c8..72a1836 100644 --- a/Projects/Stats/Stats.xcodeproj/project.pbxproj +++ b/Projects/Stats/Stats.xcodeproj/project.pbxproj @@ -287,6 +287,7 @@ DYLIB_INSTALL_NAME_BASE = "@rpath"; HEADER_SEARCH_PATHS = ( "$(inherited)", + "$(SRCROOT)/../../Tuist/.build/checkouts/FSCalendar/FSCalendar/include", "$(SRCROOT)/../../Tuist/.build/checkouts/RxSwift/Sources/RxCocoaRuntime/include", ); INFOPLIST_FILE = "Derived/InfoPlists/Stats-Info.plist"; @@ -298,11 +299,14 @@ ); OTHER_CFLAGS = ( "$(inherited)", + "-fmodule-map-file=$(SRCROOT)/../../Tuist/.build/checkouts/FSCalendar/FSCalendar/include/module.modulemap", "-fmodule-map-file=$(SRCROOT)/../../Tuist/.build/tuist-derived/RxCocoaRuntime/RxCocoaRuntime.modulemap", ); OTHER_SWIFT_FLAGS = ( "$(inherited)", "-Xcc", + "-fmodule-map-file=$(SRCROOT)/../../Tuist/.build/checkouts/FSCalendar/FSCalendar/include/module.modulemap", + "-Xcc", "-fmodule-map-file=$(SRCROOT)/../../Tuist/.build/tuist-derived/RxCocoaRuntime/RxCocoaRuntime.modulemap", ); PRODUCT_BUNDLE_IDENTIFIER = io.tuist.Stats; @@ -331,6 +335,7 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; HEADER_SEARCH_PATHS = ( "$(inherited)", + "$(SRCROOT)/../../Tuist/.build/checkouts/FSCalendar/FSCalendar/include", "$(SRCROOT)/../../Tuist/.build/checkouts/RxSwift/Sources/RxCocoaRuntime/include", ); INFOPLIST_FILE = "Derived/InfoPlists/StatsTests-Info.plist"; @@ -341,11 +346,14 @@ ); OTHER_CFLAGS = ( "$(inherited)", + "-fmodule-map-file=$(SRCROOT)/../../Tuist/.build/checkouts/FSCalendar/FSCalendar/include/module.modulemap", "-fmodule-map-file=$(SRCROOT)/../../Tuist/.build/tuist-derived/RxCocoaRuntime/RxCocoaRuntime.modulemap", ); OTHER_SWIFT_FLAGS = ( "$(inherited)", "-Xcc", + "-fmodule-map-file=$(SRCROOT)/../../Tuist/.build/checkouts/FSCalendar/FSCalendar/include/module.modulemap", + "-Xcc", "-fmodule-map-file=$(SRCROOT)/../../Tuist/.build/tuist-derived/RxCocoaRuntime/RxCocoaRuntime.modulemap", ); PRODUCT_BUNDLE_IDENTIFIER = io.tuist.StatsTests; @@ -355,7 +363,7 @@ SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; SWIFT_COMPILATION_MODE = wholemodule; - SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; + SWIFT_OPTIMIZATION_LEVEL = "-O"; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; }; @@ -479,6 +487,7 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; HEADER_SEARCH_PATHS = ( "$(inherited)", + "$(SRCROOT)/../../Tuist/.build/checkouts/FSCalendar/FSCalendar/include", "$(SRCROOT)/../../Tuist/.build/checkouts/RxSwift/Sources/RxCocoaRuntime/include", ); INFOPLIST_FILE = "Derived/InfoPlists/StatsTests-Info.plist"; @@ -489,11 +498,14 @@ ); OTHER_CFLAGS = ( "$(inherited)", + "-fmodule-map-file=$(SRCROOT)/../../Tuist/.build/checkouts/FSCalendar/FSCalendar/include/module.modulemap", "-fmodule-map-file=$(SRCROOT)/../../Tuist/.build/tuist-derived/RxCocoaRuntime/RxCocoaRuntime.modulemap", ); OTHER_SWIFT_FLAGS = ( "$(inherited)", "-Xcc", + "-fmodule-map-file=$(SRCROOT)/../../Tuist/.build/checkouts/FSCalendar/FSCalendar/include/module.modulemap", + "-Xcc", "-fmodule-map-file=$(SRCROOT)/../../Tuist/.build/tuist-derived/RxCocoaRuntime/RxCocoaRuntime.modulemap", ); PRODUCT_BUNDLE_IDENTIFIER = io.tuist.StatsTests; @@ -524,6 +536,7 @@ DYLIB_INSTALL_NAME_BASE = "@rpath"; HEADER_SEARCH_PATHS = ( "$(inherited)", + "$(SRCROOT)/../../Tuist/.build/checkouts/FSCalendar/FSCalendar/include", "$(SRCROOT)/../../Tuist/.build/checkouts/RxSwift/Sources/RxCocoaRuntime/include", ); INFOPLIST_FILE = "Derived/InfoPlists/Stats-Info.plist"; @@ -535,11 +548,14 @@ ); OTHER_CFLAGS = ( "$(inherited)", + "-fmodule-map-file=$(SRCROOT)/../../Tuist/.build/checkouts/FSCalendar/FSCalendar/include/module.modulemap", "-fmodule-map-file=$(SRCROOT)/../../Tuist/.build/tuist-derived/RxCocoaRuntime/RxCocoaRuntime.modulemap", ); OTHER_SWIFT_FLAGS = ( "$(inherited)", "-Xcc", + "-fmodule-map-file=$(SRCROOT)/../../Tuist/.build/checkouts/FSCalendar/FSCalendar/include/module.modulemap", + "-Xcc", "-fmodule-map-file=$(SRCROOT)/../../Tuist/.build/tuist-derived/RxCocoaRuntime/RxCocoaRuntime.modulemap", ); PRODUCT_BUNDLE_IDENTIFIER = io.tuist.Stats; @@ -550,7 +566,7 @@ SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; SWIFT_COMPILATION_MODE = wholemodule; - SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; + SWIFT_OPTIMIZATION_LEVEL = "-O"; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; VERSIONING_SYSTEM = "apple-generic"; diff --git a/Tuist/Package.resolved b/Tuist/Package.resolved index 6ef2d08..59230a4 100644 --- a/Tuist/Package.resolved +++ b/Tuist/Package.resolved @@ -1,5 +1,5 @@ { - "originHash" : "1beedddb3d92bd32d3b27a4f3c510d085f2cc0a86ee281f4d7bbe16f64aa3f72", + "originHash" : "4b1d9275fb8bc8040f07faf50cd79a6b4d29af7c30be4257aef2f21fe1e7e362", "pins" : [ { "identity" : "alamofire", @@ -10,6 +10,15 @@ "version" : "5.10.2" } }, + { + "identity" : "fscalendar", + "kind" : "remoteSourceControl", + "location" : "https://github.com/WenchaoD/FSCalendar.git", + "state" : { + "revision" : "0fbdec5172fccb90f707472eeaea4ffe095278f6", + "version" : "2.8.4" + } + }, { "identity" : "reactorkit", "kind" : "remoteSourceControl", diff --git a/Tuist/Package.swift b/Tuist/Package.swift index c64d3c7..59116cb 100644 --- a/Tuist/Package.swift +++ b/Tuist/Package.swift @@ -24,6 +24,7 @@ let package = Package( .package(url: "https://github.com/ReactorKit/ReactorKit.git", .upToNextMajor(from: "3.0.0")), .package(url: "https://github.com/SnapKit/SnapKit", .upToNextMajor(from: "5.0.1")), .package(url: "https://github.com/devxoul/Then", .upToNextMajor(from: "3.0.0")), - .package(url: "https://github.com/Alamofire/Alamofire", .upToNextMajor(from: "5.0.0")) - ] + .package(url: "https://github.com/Alamofire/Alamofire", .upToNextMajor(from: "5.0.0")), + .package(url: "https://github.com/WenchaoD/FSCalendar.git", from: "2.8.4") + ] )