From 379188721e26b995b53d9466d0264fd0d4cab29e Mon Sep 17 00:00:00 2001 From: Jaewon Yun Date: Wed, 28 Feb 2024 10:03:10 +0900 Subject: [PATCH 01/11] Migrate strings file to string catalog --- Resources/Domain/Localizable.xcstrings | 222 +++++++++++++ .../Localization/en.lproj/Localizable.strings | 21 -- .../Localization/ko.lproj/Localizable.strings | 21 -- .../GeneralSettings/Localizable.xcstrings | 86 +++++ .../en.lproj/Localizable.strings | 6 - .../ko.lproj/Localizable.strings | 6 - .../LanguageSetting/Localizable.xcstrings | 38 +++ .../en.lproj/Localizable.strings | 2 - .../ko.lproj/Localizable.strings | 2 - .../Localizable.xcstrings | 102 ++++++ .../en.lproj/Localizable.strings | 7 - .../ko.lproj/Localizable.strings | 7 - .../ThemeSetting/Localizable.xcstrings | 70 ++++ .../ThemeSetting/en.lproj/Localizable.strings | 4 - .../ThemeSetting/ko.lproj/Localizable.strings | 4 - .../UserSettings/Localizable.xcstrings | 198 +++++++++++ .../UserSettings/en.lproj/Localizable.strings | 15 - .../UserSettings/ko.lproj/Localizable.strings | 15 - .../WordAddition/Localizable.xcstrings | 54 +++ .../WordAddition/en.lproj/Localizable.strings | 3 - .../WordAddition/ko.lproj/Localizable.strings | 3 - .../WordChecking/Localizable.xcstrings | 312 ++++++++++++++++++ .../WordChecking/en.lproj/Localizable.strings | 20 -- .../WordChecking/ko.lproj/Localizable.strings | 20 -- .../IOSScene/WordDetail/Localizable.xcstrings | 102 ++++++ .../WordDetail/en.lproj/Localizable.strings | 6 - .../WordDetail/ko.lproj/Localizable.strings | 6 - .../IOSScene/WordList/Localizable.xcstrings | 150 +++++++++ .../WordList/en.lproj/Localizable.strings | 9 - .../WordList/ko.lproj/Localizable.strings | 9 - Resources/WordChecker/InfoPlist.xcstrings | 39 +++ .../WordChecker/en.lproj/InfoPlist.strings | 10 - .../WordChecker/ko.lproj/InfoPlist.strings | 10 - Resources/WordCheckerDev/InfoPlist.xcstrings | 40 +++ .../WordCheckerDev/en.lproj/InfoPlist.strings | 10 - .../WordCheckerDev/ko.lproj/InfoPlist.strings | 10 - Resources/iOSSupport/Localizable.xcstrings | 54 +++ .../Localization/en.lproj/Localizable.strings | 11 - .../Localization/ko.lproj/Localizable.strings | 11 - ...mainString.swift => LocalizedString.swift} | 6 +- .../UseCases/NotificationsUseCase.swift | 6 +- .../ValueObjects/TranslationLanguage.swift | 18 +- 42 files changed, 1481 insertions(+), 264 deletions(-) create mode 100644 Resources/Domain/Localizable.xcstrings delete mode 100644 Resources/Domain/Localization/en.lproj/Localizable.strings delete mode 100644 Resources/Domain/Localization/ko.lproj/Localizable.strings create mode 100644 Resources/IOSScene/GeneralSettings/Localizable.xcstrings delete mode 100644 Resources/IOSScene/GeneralSettings/en.lproj/Localizable.strings delete mode 100644 Resources/IOSScene/GeneralSettings/ko.lproj/Localizable.strings create mode 100644 Resources/IOSScene/LanguageSetting/Localizable.xcstrings delete mode 100644 Resources/IOSScene/LanguageSetting/en.lproj/Localizable.strings delete mode 100644 Resources/IOSScene/LanguageSetting/ko.lproj/Localizable.strings create mode 100644 Resources/IOSScene/PushNotificationSettings/Localizable.xcstrings delete mode 100644 Resources/IOSScene/PushNotificationSettings/en.lproj/Localizable.strings delete mode 100644 Resources/IOSScene/PushNotificationSettings/ko.lproj/Localizable.strings create mode 100644 Resources/IOSScene/ThemeSetting/Localizable.xcstrings delete mode 100644 Resources/IOSScene/ThemeSetting/en.lproj/Localizable.strings delete mode 100644 Resources/IOSScene/ThemeSetting/ko.lproj/Localizable.strings create mode 100644 Resources/IOSScene/UserSettings/Localizable.xcstrings delete mode 100644 Resources/IOSScene/UserSettings/en.lproj/Localizable.strings delete mode 100644 Resources/IOSScene/UserSettings/ko.lproj/Localizable.strings create mode 100644 Resources/IOSScene/WordAddition/Localizable.xcstrings delete mode 100644 Resources/IOSScene/WordAddition/en.lproj/Localizable.strings delete mode 100644 Resources/IOSScene/WordAddition/ko.lproj/Localizable.strings create mode 100644 Resources/IOSScene/WordChecking/Localizable.xcstrings delete mode 100644 Resources/IOSScene/WordChecking/en.lproj/Localizable.strings delete mode 100644 Resources/IOSScene/WordChecking/ko.lproj/Localizable.strings create mode 100644 Resources/IOSScene/WordDetail/Localizable.xcstrings delete mode 100644 Resources/IOSScene/WordDetail/en.lproj/Localizable.strings delete mode 100644 Resources/IOSScene/WordDetail/ko.lproj/Localizable.strings create mode 100644 Resources/IOSScene/WordList/Localizable.xcstrings delete mode 100644 Resources/IOSScene/WordList/en.lproj/Localizable.strings delete mode 100644 Resources/IOSScene/WordList/ko.lproj/Localizable.strings create mode 100644 Resources/WordChecker/InfoPlist.xcstrings delete mode 100644 Resources/WordChecker/en.lproj/InfoPlist.strings delete mode 100644 Resources/WordChecker/ko.lproj/InfoPlist.strings create mode 100644 Resources/WordCheckerDev/InfoPlist.xcstrings delete mode 100644 Resources/WordCheckerDev/en.lproj/InfoPlist.strings delete mode 100644 Resources/WordCheckerDev/ko.lproj/InfoPlist.strings create mode 100644 Resources/iOSSupport/Localizable.xcstrings delete mode 100644 Resources/iOSSupport/Localization/en.lproj/Localizable.strings delete mode 100644 Resources/iOSSupport/Localization/ko.lproj/Localizable.strings rename Sources/Domain/Localization/{DomainString.swift => LocalizedString.swift} (95%) diff --git a/Resources/Domain/Localizable.xcstrings b/Resources/Domain/Localizable.xcstrings new file mode 100644 index 00000000..6cc37c30 --- /dev/null +++ b/Resources/Domain/Localizable.xcstrings @@ -0,0 +1,222 @@ +{ + "sourceLanguage" : "en", + "strings" : { + "chinese" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Chinese" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "중국어" + } + } + } + }, + "daily_reminder" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Daily Reminder" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "매일 알림" + } + } + } + }, + "daily_reminder_body_message_%d" : { + "localizations" : { + "en" : { + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "%d word were not memorized." + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%d words were not memorized." + } + } + } + } + }, + "ko" : { + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "외우지 못한 단어가 %d개 있습니다." + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "외우지 못한 단어가 %d개 있습니다." + } + } + } + } + } + } + }, + "daily_reminder_body_message_when_no_words_to_memorize" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Memorized all the words. Please add more." + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "모든 단어를 암기했습니다. 단어를 추가해주세요." + } + } + } + }, + "english" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "English" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "영어" + } + } + } + }, + "french" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "French" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "프랑스어" + } + } + } + }, + "german" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "German" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "독일어" + } + } + } + }, + "italian" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Italian" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "이탈리아어" + } + } + } + }, + "japanese" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Japanese" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "일본어" + } + } + } + }, + "korean" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Korean" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "한국어" + } + } + } + }, + "russian" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Russian" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "러시아어" + } + } + } + }, + "spanish" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Spanish" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "스페인어" + } + } + } + } + }, + "version" : "1.0" +} \ No newline at end of file diff --git a/Resources/Domain/Localization/en.lproj/Localizable.strings b/Resources/Domain/Localization/en.lproj/Localizable.strings deleted file mode 100644 index 27cf147f..00000000 --- a/Resources/Domain/Localization/en.lproj/Localizable.strings +++ /dev/null @@ -1,21 +0,0 @@ -/* - Localizable.strings - WordChecker - - Created by Jaewon Yun on 2023/10/01. - -*/ - -korean = "Korean"; -english = "English"; -japanese = "Japanese"; -chinese = "Chinese"; -french = "French"; -spanish = "Spanish"; -italian = "Italian"; -german = "German"; -russian = "Russian"; - -daily_reminder = "Daily Reminder"; -"daily_reminder_body_message_%d" = "%d words were not memorized."; -daily_reminder_body_message_when_no_words_to_memorize = "Memorized all the words. Please add more."; diff --git a/Resources/Domain/Localization/ko.lproj/Localizable.strings b/Resources/Domain/Localization/ko.lproj/Localizable.strings deleted file mode 100644 index 779c4c1f..00000000 --- a/Resources/Domain/Localization/ko.lproj/Localizable.strings +++ /dev/null @@ -1,21 +0,0 @@ -/* - Localizable.strings - WordChecker - - Created by Jaewon Yun on 2023/10/01. - -*/ - -korean = "한국어"; -english = "영어"; -japanese = "일본어"; -chinese = "중국어"; -french = "프랑스어"; -spanish = "스페인어"; -italian = "이탈리아어"; -german = "독일어"; -russian = "러시아어"; - -daily_reminder = "매일 알림"; -"daily_reminder_body_message_%d" = "외우지 못한 단어가 %d개 있습니다."; -daily_reminder_body_message_when_no_words_to_memorize = "모든 단어를 암기했습니다. 단어를 추가해주세요."; diff --git a/Resources/IOSScene/GeneralSettings/Localizable.xcstrings b/Resources/IOSScene/GeneralSettings/Localizable.xcstrings new file mode 100644 index 00000000..f2442063 --- /dev/null +++ b/Resources/IOSScene/GeneralSettings/Localizable.xcstrings @@ -0,0 +1,86 @@ +{ + "sourceLanguage" : "en", + "strings" : { + "general" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "General" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "일반" + } + } + } + }, + "haptics" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Haptics" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "진동" + } + } + } + }, + "hapticsSettingsFooterTextWhenHapticsIsOff" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Disable haptics for interactions." + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "상호 작용에 대한 진동을 사용하지 않습니다." + } + } + } + }, + "hapticsSettingsFooterTextWhenHapticsIsOn" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Enable haptics for interactions." + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "상호 작용에 대한 진동을 사용합니다." + } + } + } + }, + "theme" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Theme" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "테마" + } + } + } + } + }, + "version" : "1.0" +} \ No newline at end of file diff --git a/Resources/IOSScene/GeneralSettings/en.lproj/Localizable.strings b/Resources/IOSScene/GeneralSettings/en.lproj/Localizable.strings deleted file mode 100644 index f88609c9..00000000 --- a/Resources/IOSScene/GeneralSettings/en.lproj/Localizable.strings +++ /dev/null @@ -1,6 +0,0 @@ -general = "General"; -haptics = "Haptics"; -theme = "Theme"; - -hapticsSettingsFooterTextWhenHapticsIsOn = "Enable haptics for interactions."; -hapticsSettingsFooterTextWhenHapticsIsOff = "Disable haptics for interactions."; diff --git a/Resources/IOSScene/GeneralSettings/ko.lproj/Localizable.strings b/Resources/IOSScene/GeneralSettings/ko.lproj/Localizable.strings deleted file mode 100644 index b7672566..00000000 --- a/Resources/IOSScene/GeneralSettings/ko.lproj/Localizable.strings +++ /dev/null @@ -1,6 +0,0 @@ -general = "일반"; -haptics = "진동"; -theme = "테마"; - -hapticsSettingsFooterTextWhenHapticsIsOn = "상호 작용에 대한 진동을 사용합니다."; -hapticsSettingsFooterTextWhenHapticsIsOff = "상호 작용에 대한 진동을 사용하지 않습니다."; diff --git a/Resources/IOSScene/LanguageSetting/Localizable.xcstrings b/Resources/IOSScene/LanguageSetting/Localizable.xcstrings new file mode 100644 index 00000000..50fec788 --- /dev/null +++ b/Resources/IOSScene/LanguageSetting/Localizable.xcstrings @@ -0,0 +1,38 @@ +{ + "sourceLanguage" : "en", + "strings" : { + "source_language" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Source language" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "원본 언어" + } + } + } + }, + "translation_language" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Translation language" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "번역 언어" + } + } + } + } + }, + "version" : "1.0" +} \ No newline at end of file diff --git a/Resources/IOSScene/LanguageSetting/en.lproj/Localizable.strings b/Resources/IOSScene/LanguageSetting/en.lproj/Localizable.strings deleted file mode 100644 index c0ecfe1e..00000000 --- a/Resources/IOSScene/LanguageSetting/en.lproj/Localizable.strings +++ /dev/null @@ -1,2 +0,0 @@ -source_language = "Source language"; -translation_language = "Translation language"; diff --git a/Resources/IOSScene/LanguageSetting/ko.lproj/Localizable.strings b/Resources/IOSScene/LanguageSetting/ko.lproj/Localizable.strings deleted file mode 100644 index c213faf1..00000000 --- a/Resources/IOSScene/LanguageSetting/ko.lproj/Localizable.strings +++ /dev/null @@ -1,2 +0,0 @@ -source_language = "원본 언어"; -translation_language = "번역 언어"; diff --git a/Resources/IOSScene/PushNotificationSettings/Localizable.xcstrings b/Resources/IOSScene/PushNotificationSettings/Localizable.xcstrings new file mode 100644 index 00000000..491ce54e --- /dev/null +++ b/Resources/IOSScene/PushNotificationSettings/Localizable.xcstrings @@ -0,0 +1,102 @@ +{ + "sourceLanguage" : "en", + "strings" : { + "allow_notifications_is_required" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Allow notifications is required." + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "알림 허용이 필요합니다." + } + } + } + }, + "daily_reminder" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Daily Reminder" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "매일 알림" + } + } + } + }, + "dailyReminderFooter" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sends a daily push notification at the time you set." + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "설정한 시각에 매일 푸시 알림을 보냅니다." + } + } + } + }, + "notice" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Notice" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "알림" + } + } + } + }, + "notifications" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Notifications" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "알림" + } + } + } + }, + "time" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Time" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "시간" + } + } + } + } + }, + "version" : "1.0" +} \ No newline at end of file diff --git a/Resources/IOSScene/PushNotificationSettings/en.lproj/Localizable.strings b/Resources/IOSScene/PushNotificationSettings/en.lproj/Localizable.strings deleted file mode 100644 index 6650ac17..00000000 --- a/Resources/IOSScene/PushNotificationSettings/en.lproj/Localizable.strings +++ /dev/null @@ -1,7 +0,0 @@ -daily_reminder = "Daily Reminder"; -notifications = "Notifications"; -notice = "Notice"; -time = "Time"; - -allow_notifications_is_required = "Allow notifications is required."; -dailyReminderFooter = "Sends a daily push notification at the time you set."; diff --git a/Resources/IOSScene/PushNotificationSettings/ko.lproj/Localizable.strings b/Resources/IOSScene/PushNotificationSettings/ko.lproj/Localizable.strings deleted file mode 100644 index 4aab8648..00000000 --- a/Resources/IOSScene/PushNotificationSettings/ko.lproj/Localizable.strings +++ /dev/null @@ -1,7 +0,0 @@ -daily_reminder = "매일 알림"; -notifications = "알림"; -notice = "알림"; -time = "시간"; - -allow_notifications_is_required = "알림 허용이 필요합니다."; -dailyReminderFooter = "설정한 시각에 매일 푸시 알림을 보냅니다."; diff --git a/Resources/IOSScene/ThemeSetting/Localizable.xcstrings b/Resources/IOSScene/ThemeSetting/Localizable.xcstrings new file mode 100644 index 00000000..15fd573e --- /dev/null +++ b/Resources/IOSScene/ThemeSetting/Localizable.xcstrings @@ -0,0 +1,70 @@ +{ + "sourceLanguage" : "en", + "strings" : { + "dark_mode" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Dark mode" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "다크 모드" + } + } + } + }, + "light_mode" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Light mode" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "라이트 모드" + } + } + } + }, + "system_mode" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "System mode" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "시스템 설정 모드" + } + } + } + }, + "theme" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Theme" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "테마" + } + } + } + } + }, + "version" : "1.0" +} \ No newline at end of file diff --git a/Resources/IOSScene/ThemeSetting/en.lproj/Localizable.strings b/Resources/IOSScene/ThemeSetting/en.lproj/Localizable.strings deleted file mode 100644 index 2479f564..00000000 --- a/Resources/IOSScene/ThemeSetting/en.lproj/Localizable.strings +++ /dev/null @@ -1,4 +0,0 @@ -theme = "Theme"; -system_mode = "System mode"; -light_mode = "Light mode"; -dark_mode = "Dark mode"; diff --git a/Resources/IOSScene/ThemeSetting/ko.lproj/Localizable.strings b/Resources/IOSScene/ThemeSetting/ko.lproj/Localizable.strings deleted file mode 100644 index 5521f4f1..00000000 --- a/Resources/IOSScene/ThemeSetting/ko.lproj/Localizable.strings +++ /dev/null @@ -1,4 +0,0 @@ -theme = "테마"; -system_mode = "시스템 설정 모드"; -light_mode = "라이트 모드"; -dark_mode = "다크 모드"; diff --git a/Resources/IOSScene/UserSettings/Localizable.xcstrings b/Resources/IOSScene/UserSettings/Localizable.xcstrings new file mode 100644 index 00000000..f117571d --- /dev/null +++ b/Resources/IOSScene/UserSettings/Localizable.xcstrings @@ -0,0 +1,198 @@ +{ + "sourceLanguage" : "en", + "strings" : { + "general" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "General" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "일반" + } + } + } + }, + "google_drive_download" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Download data from Google Drive" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "구글 드라이브에서 데이터 다운로드" + } + } + } + }, + "google_drive_download_successfully" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Successfully downloading from Google Drive" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "구글 드라이브에서 다운로드 성공" + } + } + } + }, + "google_drive_logout" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Google Drive Logout" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "구글 드라이브 로그아웃" + } + } + } + }, + "google_drive_upload" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Upload data to Google Drive" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "구글 드라이브에 데이터 업로드" + } + } + } + }, + "google_drive_upload_successfully" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Successfully uploading to Google Drive" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "구글 드라이브에 업로드 성공" + } + } + } + }, + "notice" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Notice" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "알림" + } + } + } + }, + "notifications" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Notifications" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "알림" + } + } + } + }, + "settings" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Settings" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "설정" + } + } + } + }, + "signed_out_of_google_drive_successfully" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Signed out of Google Drive successfully" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "구글 드라이브에서 로그아웃되었습니다." + } + } + } + }, + "source_language" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Source language" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "원본 언어" + } + } + } + }, + "translation_language" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Translation language" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "번역 언어" + } + } + } + } + }, + "version" : "1.0" +} \ No newline at end of file diff --git a/Resources/IOSScene/UserSettings/en.lproj/Localizable.strings b/Resources/IOSScene/UserSettings/en.lproj/Localizable.strings deleted file mode 100644 index c7b3b060..00000000 --- a/Resources/IOSScene/UserSettings/en.lproj/Localizable.strings +++ /dev/null @@ -1,15 +0,0 @@ -source_language = "Source language"; -translation_language = "Translation language"; -notifications = "Notifications"; -general = "General"; -settings = "Settings"; -notice = "Notice"; - -google_drive_upload = "Upload data to Google Drive"; -google_drive_upload_successfully = "Successfully uploading to Google Drive"; - -google_drive_download = "Download data from Google Drive"; -google_drive_download_successfully = "Successfully downloading from Google Drive"; - -google_drive_logout = "Google Drive Logout"; -signed_out_of_google_drive_successfully = "Signed out of Google Drive successfully"; diff --git a/Resources/IOSScene/UserSettings/ko.lproj/Localizable.strings b/Resources/IOSScene/UserSettings/ko.lproj/Localizable.strings deleted file mode 100644 index bf4ec6f3..00000000 --- a/Resources/IOSScene/UserSettings/ko.lproj/Localizable.strings +++ /dev/null @@ -1,15 +0,0 @@ -source_language = "원본 언어"; -translation_language = "번역 언어"; -notifications = "알림"; -general = "일반"; -settings = "설정"; -notice = "알림"; - -google_drive_upload = "구글 드라이브에 데이터 업로드"; -google_drive_upload_successfully = "구글 드라이브에 업로드 성공"; - -google_drive_download = "구글 드라이브에서 데이터 다운로드"; -google_drive_download_successfully = "구글 드라이브에서 다운로드 성공"; - -google_drive_logout = "구글 드라이브 로그아웃"; -signed_out_of_google_drive_successfully = "구글 드라이브에서 로그아웃되었습니다."; diff --git a/Resources/IOSScene/WordAddition/Localizable.xcstrings b/Resources/IOSScene/WordAddition/Localizable.xcstrings new file mode 100644 index 00000000..98b7cc4d --- /dev/null +++ b/Resources/IOSScene/WordAddition/Localizable.xcstrings @@ -0,0 +1,54 @@ +{ + "sourceLanguage" : "en", + "strings" : { + "duplicate_word" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Duplicate word." + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "중복 단어입니다." + } + } + } + }, + "newWord" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "New word" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "새로운 단어" + } + } + } + }, + "word" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Word" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "단어" + } + } + } + } + }, + "version" : "1.0" +} \ No newline at end of file diff --git a/Resources/IOSScene/WordAddition/en.lproj/Localizable.strings b/Resources/IOSScene/WordAddition/en.lproj/Localizable.strings deleted file mode 100644 index 4cd4e4c1..00000000 --- a/Resources/IOSScene/WordAddition/en.lproj/Localizable.strings +++ /dev/null @@ -1,3 +0,0 @@ -word = "Word"; -duplicate_word = "Duplicate word."; -newWord = "New word"; diff --git a/Resources/IOSScene/WordAddition/ko.lproj/Localizable.strings b/Resources/IOSScene/WordAddition/ko.lproj/Localizable.strings deleted file mode 100644 index ae9df8b1..00000000 --- a/Resources/IOSScene/WordAddition/ko.lproj/Localizable.strings +++ /dev/null @@ -1,3 +0,0 @@ -word = "단어"; -duplicate_word = "중복 단어입니다."; -newWord = "새로운 단어"; diff --git a/Resources/IOSScene/WordChecking/Localizable.xcstrings b/Resources/IOSScene/WordChecking/Localizable.xcstrings new file mode 100644 index 00000000..412f70d2 --- /dev/null +++ b/Resources/IOSScene/WordChecking/Localizable.xcstrings @@ -0,0 +1,312 @@ +{ + "sourceLanguage" : "en", + "strings" : { + "%@_added_failed" : { + "comment" : "알 수 없는 이유로 단어 추가 실패 후 표시되는 메세지", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "[ %@ ] added failed." + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "[ %@ ] 단어 추가 실패" + } + } + } + }, + "%@_added_successfully" : { + "comment" : "단어 추가 완료 후 표시되는 메세지", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "[ %@ ] added successfully" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "[ %@ ] 추가 성공" + } + } + } + }, + "add" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Add" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "추가" + } + } + } + }, + "addWord" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Add word" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "단어 추가" + } + } + } + }, + "already_added_word" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Already added word." + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "이미 추가된 단어입니다." + } + } + } + }, + "cancel" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Cancel" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "취소" + } + } + } + }, + "deleteWord" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Delete word" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "단어 삭제" + } + } + } + }, + "memorize_words" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Memorize words" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "단어 암기" + } + } + } + }, + "memorized" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Memorized" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "암기 완료" + } + } + } + }, + "more_menu" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "More menu" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "메뉴 더보기" + } + } + } + }, + "next_word" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Next word" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "다음 단어" + } + } + } + }, + "notice" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Notice" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "알림" + } + } + } + }, + "noWords" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "No words" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "단어 없음" + } + } + } + }, + "please_check_your_network_connection" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Please check your network connection." + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "네트워크 연결 상태를 확인해 주세요." + } + } + } + }, + "previous_word" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Previous word" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "이전 단어" + } + } + } + }, + "quick_add_word" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Quick add word" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "빠른 단어 추가" + } + } + } + }, + "shuffleOrder" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Shuffle order" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "순서 섞기" + } + } + } + }, + "translate" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Translate" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "번역" + } + } + } + }, + "translation_site_alert_message" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "There is a problem with your current translation site. Please go to Settings and set it to a different translation site." + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "현재 번역 사이트에 문제가 있습니다. 설정으로 이동하여 다른 번역 사이트로 설정해주세요." + } + } + } + } + }, + "version" : "1.0" +} \ No newline at end of file diff --git a/Resources/IOSScene/WordChecking/en.lproj/Localizable.strings b/Resources/IOSScene/WordChecking/en.lproj/Localizable.strings deleted file mode 100644 index 8eb3fdf0..00000000 --- a/Resources/IOSScene/WordChecking/en.lproj/Localizable.strings +++ /dev/null @@ -1,20 +0,0 @@ -add = "Add"; -cancel = "Cancel"; -addWord = "Add word"; -more_menu = "More menu"; -memorize_words = "Memorize words"; -memorized = "Memorized"; -shuffleOrder = "Shuffle order"; -deleteWord = "Delete word"; -please_check_your_network_connection = "Please check your network connection."; -noWords = "No words"; -quick_add_word = "Quick add word"; -translate = "Translate"; -next_word = "Next word"; -previous_word = "Previous word"; -already_added_word = "Already added word."; -notice = "Notice"; -translation_site_alert_message = "There is a problem with your current translation site. Please go to Settings and set it to a different translation site."; - -"%@_added_failed" = "[ %@ ] added failed."; -"%@_added_successfully" = "[ %@ ] added successfully"; diff --git a/Resources/IOSScene/WordChecking/ko.lproj/Localizable.strings b/Resources/IOSScene/WordChecking/ko.lproj/Localizable.strings deleted file mode 100644 index 48c7ce50..00000000 --- a/Resources/IOSScene/WordChecking/ko.lproj/Localizable.strings +++ /dev/null @@ -1,20 +0,0 @@ -add = "추가"; -cancel = "취소"; -addWord = "단어 추가"; -more_menu = "메뉴 더보기"; -memorize_words = "단어 암기"; -memorized = "암기 완료"; -shuffleOrder = "순서 섞기"; -deleteWord = "단어 삭제"; -please_check_your_network_connection = "네트워크 연결 상태를 확인해 주세요."; -noWords = "단어 없음"; -quick_add_word = "빠른 단어 추가"; -translate = "번역"; -next_word = "다음 단어"; -previous_word = "이전 단어"; -already_added_word = "이미 추가된 단어입니다."; -notice = "알림"; -translation_site_alert_message = "현재 번역 사이트에 문제가 있습니다. 설정으로 이동하여 다른 번역 사이트로 설정해주세요."; - -"%@_added_failed" = "[ %@ ] 단어 추가 실패"; -"%@_added_successfully" = "[ %@ ] 추가 성공"; diff --git a/Resources/IOSScene/WordDetail/Localizable.xcstrings b/Resources/IOSScene/WordDetail/Localizable.xcstrings new file mode 100644 index 00000000..bde7f57f --- /dev/null +++ b/Resources/IOSScene/WordDetail/Localizable.xcstrings @@ -0,0 +1,102 @@ +{ + "sourceLanguage" : "en", + "strings" : { + "details" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Details" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "세부사항" + } + } + } + }, + "done" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Done" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "완료" + } + } + } + }, + "duplicate_word" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Duplicate word." + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "중복 단어입니다." + } + } + } + }, + "memorized" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Memorized" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "암기 완료" + } + } + } + }, + "memorizing" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Memorizing" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "암기중" + } + } + } + }, + "word" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Word" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "단어" + } + } + } + } + }, + "version" : "1.0" +} \ No newline at end of file diff --git a/Resources/IOSScene/WordDetail/en.lproj/Localizable.strings b/Resources/IOSScene/WordDetail/en.lproj/Localizable.strings deleted file mode 100644 index e7b9dbb5..00000000 --- a/Resources/IOSScene/WordDetail/en.lproj/Localizable.strings +++ /dev/null @@ -1,6 +0,0 @@ -word = "Word"; -duplicate_word = "Duplicate word."; -done = "Done"; -details = "Details"; -memorizing = "Memorizing"; -memorized = "Memorized"; diff --git a/Resources/IOSScene/WordDetail/ko.lproj/Localizable.strings b/Resources/IOSScene/WordDetail/ko.lproj/Localizable.strings deleted file mode 100644 index 4111dd96..00000000 --- a/Resources/IOSScene/WordDetail/ko.lproj/Localizable.strings +++ /dev/null @@ -1,6 +0,0 @@ -word = "단어"; -duplicate_word = "중복 단어입니다."; -done = "완료"; -details = "세부사항"; -memorizing = "암기중"; -memorized = "암기 완료"; diff --git a/Resources/IOSScene/WordList/Localizable.xcstrings b/Resources/IOSScene/WordList/Localizable.xcstrings new file mode 100644 index 00000000..518dbc2d --- /dev/null +++ b/Resources/IOSScene/WordList/Localizable.xcstrings @@ -0,0 +1,150 @@ +{ + "sourceLanguage" : "en", + "strings" : { + "addWord" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Add word" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "단어 추가" + } + } + } + }, + "all" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "All" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "전체" + } + } + } + }, + "cancel" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Cancel" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "취소" + } + } + } + }, + "delete" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Delete" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "삭제" + } + } + } + }, + "edit" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Edit" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "수정" + } + } + } + }, + "editWord" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Edit word" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "단어 수정" + } + } + } + }, + "memorized" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Memorized" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "암기 완료" + } + } + } + }, + "memorizing" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Memorizing" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "암기중" + } + } + } + }, + "there_are_no_words" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "There are no words." + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "단어가 없습니다." + } + } + } + } + }, + "version" : "1.0" +} \ No newline at end of file diff --git a/Resources/IOSScene/WordList/en.lproj/Localizable.strings b/Resources/IOSScene/WordList/en.lproj/Localizable.strings deleted file mode 100644 index 252dc3cf..00000000 --- a/Resources/IOSScene/WordList/en.lproj/Localizable.strings +++ /dev/null @@ -1,9 +0,0 @@ -addWord = "Add word"; -all = "All"; -there_are_no_words = "There are no words."; -memorizing = "Memorizing"; -memorized = "Memorized"; -delete = "Delete"; -edit = "Edit"; -editWord = "Edit word"; -cancel = "Cancel"; diff --git a/Resources/IOSScene/WordList/ko.lproj/Localizable.strings b/Resources/IOSScene/WordList/ko.lproj/Localizable.strings deleted file mode 100644 index 7da1b889..00000000 --- a/Resources/IOSScene/WordList/ko.lproj/Localizable.strings +++ /dev/null @@ -1,9 +0,0 @@ -addWord = "단어 추가"; -all = "전체"; -there_are_no_words = "단어가 없습니다."; -memorizing = "암기중"; -memorized = "암기 완료"; -delete = "삭제"; -edit = "수정"; -editWord = "단어 수정"; -cancel = "취소"; diff --git a/Resources/WordChecker/InfoPlist.xcstrings b/Resources/WordChecker/InfoPlist.xcstrings new file mode 100644 index 00000000..f5a13393 --- /dev/null +++ b/Resources/WordChecker/InfoPlist.xcstrings @@ -0,0 +1,39 @@ +{ + "sourceLanguage" : "en", + "strings" : { + "CFBundleDisplayName" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Memorizer" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "외우미" + } + } + } + }, + "CFBundleName" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Memorizer" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "외우미" + } + } + } + } + }, + "version" : "1.0" +} diff --git a/Resources/WordChecker/en.lproj/InfoPlist.strings b/Resources/WordChecker/en.lproj/InfoPlist.strings deleted file mode 100644 index aa0d56f9..00000000 --- a/Resources/WordChecker/en.lproj/InfoPlist.strings +++ /dev/null @@ -1,10 +0,0 @@ -/* - InfoPlist.strings - WordChecker - - Created by Jaewon Yun on 2023/09/13. - Copyright © 2023 woin2ee. All rights reserved. -*/ - -CFBundleName = "Memorizer"; -CFBundleDisplayName = "Memorizer"; diff --git a/Resources/WordChecker/ko.lproj/InfoPlist.strings b/Resources/WordChecker/ko.lproj/InfoPlist.strings deleted file mode 100644 index 27e6c700..00000000 --- a/Resources/WordChecker/ko.lproj/InfoPlist.strings +++ /dev/null @@ -1,10 +0,0 @@ -/* - InfoPlist.strings - WordChecker - - Created by Jaewon Yun on 2023/09/13. - Copyright © 2023 woin2ee. All rights reserved. -*/ - -CFBundleName = "외우미"; -CFBundleDisplayName = "외우미"; diff --git a/Resources/WordCheckerDev/InfoPlist.xcstrings b/Resources/WordCheckerDev/InfoPlist.xcstrings new file mode 100644 index 00000000..e27b7ebe --- /dev/null +++ b/Resources/WordCheckerDev/InfoPlist.xcstrings @@ -0,0 +1,40 @@ +{ + "sourceLanguage" : "en", + "strings" : { + "CFBundleDisplayName" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Memorizer_Dev" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "외우미_Dev" + } + } + } + }, + "CFBundleName" : { + "comment" : "Bundle name", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Memorizer_Dev" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "외우미_Dev" + } + } + } + } + }, + "version" : "1.0" +} \ No newline at end of file diff --git a/Resources/WordCheckerDev/en.lproj/InfoPlist.strings b/Resources/WordCheckerDev/en.lproj/InfoPlist.strings deleted file mode 100644 index 3290a413..00000000 --- a/Resources/WordCheckerDev/en.lproj/InfoPlist.strings +++ /dev/null @@ -1,10 +0,0 @@ -/* - InfoPlist.strings - WordChecker - - Created by Jaewon Yun on 2023/09/13. - Copyright © 2023 woin2ee. All rights reserved. -*/ - -CFBundleName = "Memorizer_Dev"; -CFBundleDisplayName = "Memorizer_Dev"; diff --git a/Resources/WordCheckerDev/ko.lproj/InfoPlist.strings b/Resources/WordCheckerDev/ko.lproj/InfoPlist.strings deleted file mode 100644 index f3a4648c..00000000 --- a/Resources/WordCheckerDev/ko.lproj/InfoPlist.strings +++ /dev/null @@ -1,10 +0,0 @@ -/* - InfoPlist.strings - WordChecker - - Created by Jaewon Yun on 2023/09/13. - Copyright © 2023 woin2ee. All rights reserved. -*/ - -CFBundleName = "외우미_Dev"; -CFBundleDisplayName = "외우미_Dev"; diff --git a/Resources/iOSSupport/Localizable.xcstrings b/Resources/iOSSupport/Localizable.xcstrings new file mode 100644 index 00000000..12e425b2 --- /dev/null +++ b/Resources/iOSSupport/Localizable.xcstrings @@ -0,0 +1,54 @@ +{ + "sourceLanguage" : "en", + "strings" : { + "cancel" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Cancel" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "취소" + } + } + } + }, + "discardChanges" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Discard Changes" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "변경 사항 폐기" + } + } + } + }, + "ok" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ok" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "확인" + } + } + } + } + }, + "version" : "1.0" +} diff --git a/Resources/iOSSupport/Localization/en.lproj/Localizable.strings b/Resources/iOSSupport/Localization/en.lproj/Localizable.strings deleted file mode 100644 index 7d702d30..00000000 --- a/Resources/iOSSupport/Localization/en.lproj/Localizable.strings +++ /dev/null @@ -1,11 +0,0 @@ -/* - Localizable.strings - WordChecker - - Created by Jaewon Yun on 2023/08/23. - -*/ - -ok = "Ok"; -cancel = "Cancel"; -discardChanges = "Discard Changes"; diff --git a/Resources/iOSSupport/Localization/ko.lproj/Localizable.strings b/Resources/iOSSupport/Localization/ko.lproj/Localizable.strings deleted file mode 100644 index 000ec888..00000000 --- a/Resources/iOSSupport/Localization/ko.lproj/Localizable.strings +++ /dev/null @@ -1,11 +0,0 @@ -/* - Localizable.strings - WordChecker - - Created by Jaewon Yun on 2023/08/23. - -*/ - -ok = "확인"; -cancel = "취소"; -discardChanges = "변경 사항 폐기"; diff --git a/Sources/Domain/Localization/DomainString.swift b/Sources/Domain/Localization/LocalizedString.swift similarity index 95% rename from Sources/Domain/Localization/DomainString.swift rename to Sources/Domain/Localization/LocalizedString.swift index d520b952..d5f8cdab 100644 --- a/Sources/Domain/Localization/DomainString.swift +++ b/Sources/Domain/Localization/LocalizedString.swift @@ -1,5 +1,5 @@ // -// DomainString.swift +// LocalizedString.swift // Domain // // Created by Jaewon Yun on 2023/10/01. @@ -8,9 +8,7 @@ import Foundation -struct DomainString { - - private init() {} +enum LocalizedString { static let korean = NSLocalizedString("korean", bundle: Bundle.module, comment: "") static let english = NSLocalizedString("english", bundle: Bundle.module, comment: "") diff --git a/Sources/Domain/UseCases/NotificationsUseCase.swift b/Sources/Domain/UseCases/NotificationsUseCase.swift index a78fb888..52679471 100644 --- a/Sources/Domain/UseCases/NotificationsUseCase.swift +++ b/Sources/Domain/UseCases/NotificationsUseCase.swift @@ -57,13 +57,13 @@ final class NotificationsUseCase: NotificationsUseCaseProtocol { let unmemorizedWordCount = self.wordRepository.getUnmemorizedList().count let content: UNMutableNotificationContent = .init() - content.title = DomainString.daily_reminder + content.title = LocalizedString.daily_reminder content.sound = .default if unmemorizedWordCount == 0 { - content.body = DomainString.daily_reminder_body_message_when_no_words_to_memorize + content.body = LocalizedString.daily_reminder_body_message_when_no_words_to_memorize } else { - content.body = DomainString.daily_reminder_body_message(unmemorizedWordCount: unmemorizedWordCount) + content.body = LocalizedString.daily_reminder_body_message(unmemorizedWordCount: unmemorizedWordCount) } let trigger: UNCalendarNotificationTrigger = .init(dateMatching: time, repeats: true) diff --git a/Sources/Domain/ValueObjects/TranslationLanguage.swift b/Sources/Domain/ValueObjects/TranslationLanguage.swift index 01ff408f..b53b5461 100644 --- a/Sources/Domain/ValueObjects/TranslationLanguage.swift +++ b/Sources/Domain/ValueObjects/TranslationLanguage.swift @@ -32,23 +32,23 @@ public enum TranslationLanguage: CaseIterable, Codable, Sendable { public var localizedString: String { switch self { case .korean: - DomainString.korean + LocalizedString.korean case .english: - DomainString.english + LocalizedString.english case .japanese: - DomainString.japanese + LocalizedString.japanese case .chinese: - DomainString.chinese + LocalizedString.chinese case .french: - DomainString.french + LocalizedString.french case .spanish: - DomainString.spanish + LocalizedString.spanish case .italian: - DomainString.italian + LocalizedString.italian case .german: - DomainString.german + LocalizedString.german case .russian: - DomainString.russian + LocalizedString.russian } } From a17256f66223436a73b416444136980bbff5d2a9 Mon Sep 17 00:00:00 2001 From: Jaewon Yun Date: Wed, 28 Feb 2024 11:13:51 +0900 Subject: [PATCH 02/11] Make specification object shared --- .../DI/Specifications/WordDuplicateSpecificationAssembly.swift | 1 + Sources/Domain/Specifications/WordDuplicateSpecification.swift | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/Sources/Domain/DI/Specifications/WordDuplicateSpecificationAssembly.swift b/Sources/Domain/DI/Specifications/WordDuplicateSpecificationAssembly.swift index 6ca0ba1c..c271e19b 100644 --- a/Sources/Domain/DI/Specifications/WordDuplicateSpecificationAssembly.swift +++ b/Sources/Domain/DI/Specifications/WordDuplicateSpecificationAssembly.swift @@ -15,5 +15,6 @@ struct WordDuplicateSpecificationAssembly: Assembly { container.register(WordDuplicateSpecification.self) { resolver in return .init(wordRepository: resolver.resolve()) } + .inObjectScope(.container) } } diff --git a/Sources/Domain/Specifications/WordDuplicateSpecification.swift b/Sources/Domain/Specifications/WordDuplicateSpecification.swift index 140700c6..a132bece 100644 --- a/Sources/Domain/Specifications/WordDuplicateSpecification.swift +++ b/Sources/Domain/Specifications/WordDuplicateSpecification.swift @@ -13,7 +13,7 @@ import RxSwift /// 대소문자가 다른 단어는 같은 단어로 취급합니다. /// /// - Note: UUID 가 동일한 Entity 는 하나의 Entity 이므로 중복으로 인식되지 않습니다. -struct WordDuplicateSpecification: Specification { +final class WordDuplicateSpecification: Specification { private let wordRepository: WordRepositoryProtocol From 4b880bd7683e78e06af9f976475fdcef651953ff Mon Sep 17 00:00:00 2001 From: Jaewon Yun Date: Wed, 28 Feb 2024 16:02:46 +0900 Subject: [PATCH 03/11] Refactor WordUseCase --- Sources/Domain/Interfaces/Specification.swift | 20 +---- .../UseCases/Word/WordUseCaseProtocol.swift | 16 ++-- .../WordDuplicateSpecification.swift | 10 +-- Sources/Domain/UseCases/WordUseCase.swift | 65 ++++---------- Sources/DomainTesting/WordUseCaseFake.swift | 33 +++---- .../WordChecking/WordCheckingReactor.swift | 88 +++++++++---------- .../WordCheckingViewController.swift | 3 +- .../WordDetail/WordDetailReactor.swift | 2 +- .../IOSScene/WordList/WordListReactor.swift | 12 ++- Tests/DomainTests/WordUseCaseTests.swift | 38 ++++---- .../WordCheckerUITests.swift | 1 + 11 files changed, 113 insertions(+), 175 deletions(-) diff --git a/Sources/Domain/Interfaces/Specification.swift b/Sources/Domain/Interfaces/Specification.swift index 43cd21a3..72d2df8c 100644 --- a/Sources/Domain/Interfaces/Specification.swift +++ b/Sources/Domain/Interfaces/Specification.swift @@ -6,27 +6,11 @@ // Copyright © 2024 woin2ee. All rights reserved. // -import RxSwift -import Utility - protocol Specification { associatedtype Entity + associatedtype Result /// `Entity` 가 Specification 을 만족하는지 여부를 반환합니다. - func isSatisfied(for entity: Entity) -> Infallible - - /// `Entity` 가 Specification 을 만족하는지 여부를 반환합니다. - func isSatisfied(for entity: Entity) -> Bool -} - -extension Specification { - - func isSatisfied(for entity: Entity) -> Infallible { - abstractMethod() - } - - func isSatisfied(for entity: Entity) -> Bool { - abstractMethod() - } + func isSatisfied(for entity: Entity) -> Result } diff --git a/Sources/Domain/Interfaces/UseCases/Word/WordUseCaseProtocol.swift b/Sources/Domain/Interfaces/UseCases/Word/WordUseCaseProtocol.swift index 58254ffe..15485199 100644 --- a/Sources/Domain/Interfaces/UseCases/Word/WordUseCaseProtocol.swift +++ b/Sources/Domain/Interfaces/UseCases/Word/WordUseCaseProtocol.swift @@ -19,30 +19,30 @@ public protocol WordUseCaseProtocol { func deleteWord(by uuid: UUID) -> Single /// 저장된 모든 단어 목록을 가져옵니다. - func getWordList() -> Single<[Word]> + func fetchWordList() -> [Word] /// 암기 완료된 단어의 목록을 가져옵니다. - func getMemorizedWordList() -> Single<[Word]> + func fetchMemorizedWordList() -> [Word] /// 암기되지 않은 단어의 목록을 가져옵니다. - func getUnmemorizedWordList() -> Single<[Word]> + func fetchUnmemorizedWordList() -> [Word] /// 특정 단어를 가져옵니다. - func getWord(by uuid: UUID) -> Single + func fetchWord(by uuid: UUID) -> Single /// 단어를 업데이트 합니다. func updateWord(by uuid: UUID, to newWord: Word) -> Single /// 암기되지 않은 단어 목록을 섞습니다. - func shuffleUnmemorizedWordList() -> Single + func shuffleUnmemorizedWordList() - func updateToNextWord() -> Single + func updateToNextWord() - func updateToPreviousWord() -> Single + func updateToPreviousWord() func markCurrentWordAsMemorized(uuid: UUID) -> Single - func getCurrentUnmemorizedWord() -> Infallible + func getCurrentUnmemorizedWord() -> Word? /// `word` 파라미터로 전달된 단어가 이미 저장된 단어인지 검사합니다. /// diff --git a/Sources/Domain/Specifications/WordDuplicateSpecification.swift b/Sources/Domain/Specifications/WordDuplicateSpecification.swift index a132bece..0c3dd08d 100644 --- a/Sources/Domain/Specifications/WordDuplicateSpecification.swift +++ b/Sources/Domain/Specifications/WordDuplicateSpecification.swift @@ -8,11 +8,6 @@ import RxSwift -/// 단어가 중복되면 안됩니다. -/// -/// 대소문자가 다른 단어는 같은 단어로 취급합니다. -/// -/// - Note: UUID 가 동일한 Entity 는 하나의 Entity 이므로 중복으로 인식되지 않습니다. final class WordDuplicateSpecification: Specification { private let wordRepository: WordRepositoryProtocol @@ -21,6 +16,11 @@ final class WordDuplicateSpecification: Specification { self.wordRepository = wordRepository } + /// 단어가 중복이 아니면 `true` 를 반환합니다. + /// + /// 대소문자가 다른 단어는 같은 단어로 취급합니다. + /// + /// - Note: UUID 가 동일한 Entity 는 하나의 Entity 이므로 중복으로 인식되지 않습니다. func isSatisfied(for entity: Word) -> Bool { if let originWord = wordRepository.getWord(by: entity.uuid) { // Entity 가 이미 있을때는 업데이트 상황이므로 만약 중복 단어가 존재한다면 추가로 자기 자신 Entity 가 아닌지 확인이 필요합니다. diff --git a/Sources/Domain/UseCases/WordUseCase.swift b/Sources/Domain/UseCases/WordUseCase.swift index fb1c2a0b..f64465a2 100644 --- a/Sources/Domain/UseCases/WordUseCase.swift +++ b/Sources/Domain/UseCases/WordUseCase.swift @@ -61,34 +61,19 @@ public final class WordUseCase: WordUseCaseProtocol { return .just(()) } - public func getWordList() -> RxSwift.Single<[Word]> { - return .create { single in - let wordList = self.wordRepository.getAllWords() - single(.success(wordList)) - - return Disposables.create() - } + public func fetchWordList() -> [Word] { + return wordRepository.getAllWords() } - public func getUnmemorizedWordList() -> Single<[Word]> { - return .create { single in - let unmemorizedList = self.wordRepository.getUnmemorizedList() - single(.success(unmemorizedList)) - - return Disposables.create() - } + public func fetchUnmemorizedWordList() -> [Word] { + return wordRepository.getUnmemorizedList() } - public func getMemorizedWordList() -> Single<[Word]> { - return .create { single in - let memorizedList = self.wordRepository.getMemorizedList() - single(.success(memorizedList)) - - return Disposables.create() - } + public func fetchMemorizedWordList() -> [Word] { + return wordRepository.getMemorizedList() } - public func getWord(by uuid: UUID) -> RxSwift.Single { + public func fetchWord(by uuid: UUID) -> RxSwift.Single { return .create { single in if let word = self.wordRepository.getWord(by: uuid) { single(.success(word)) @@ -134,35 +119,17 @@ public final class WordUseCase: WordUseCaseProtocol { return .just(()) } - public func shuffleUnmemorizedWordList() -> RxSwift.Single { - return .create { single in - let unmemorizedList = self.wordRepository.getUnmemorizedList() - self.unmemorizedWordListRepository.shuffle(with: unmemorizedList) - - single(.success(())) - - return Disposables.create() - } + public func shuffleUnmemorizedWordList() { + let unmemorizedList = wordRepository.getUnmemorizedList() + unmemorizedWordListRepository.shuffle(with: unmemorizedList) } - public func updateToNextWord() -> RxSwift.Single { - return .create { single in - self.unmemorizedWordListRepository.updateToNextWord() - - single(.success(())) - - return Disposables.create() - } + public func updateToNextWord() { + unmemorizedWordListRepository.updateToNextWord() } - public func updateToPreviousWord() -> RxSwift.Single { - return .create { single in - self.unmemorizedWordListRepository.updateToPreviousWord() - - single(.success(())) - - return Disposables.create() - } + public func updateToPreviousWord() { + unmemorizedWordListRepository.updateToPreviousWord() } public func markCurrentWordAsMemorized(uuid: UUID) -> RxSwift.Single { @@ -183,8 +150,8 @@ public final class WordUseCase: WordUseCaseProtocol { return .just(()) } - public func getCurrentUnmemorizedWord() -> Infallible { - return .just(unmemorizedWordListRepository.getCurrentWord()) + public func getCurrentUnmemorizedWord() -> Word? { + return unmemorizedWordListRepository.getCurrentWord() } public func isWordDuplicated(_ word: String) -> Single { diff --git a/Sources/DomainTesting/WordUseCaseFake.swift b/Sources/DomainTesting/WordUseCaseFake.swift index 8bbbdddc..7521712a 100644 --- a/Sources/DomainTesting/WordUseCaseFake.swift +++ b/Sources/DomainTesting/WordUseCaseFake.swift @@ -40,21 +40,21 @@ public final class WordUseCaseFake: WordUseCaseProtocol { return .just(()) } - public func getWordList() -> Single<[Domain.Word]> { - return .just(_wordList) + public func fetchWordList() -> [Domain.Word] { + return _wordList } - public func getMemorizedWordList() -> Single<[Domain.Word]> { + public func fetchMemorizedWordList() -> [Domain.Word] { let memorizedList = _wordList.filter { $0.memorizedState == .memorized } - return .just(memorizedList) + return memorizedList } - public func getUnmemorizedWordList() -> Single<[Domain.Word]> { + public func fetchUnmemorizedWordList() -> [Domain.Word] { let memorizingList = _wordList.filter { $0.memorizedState == .memorizing } - return .just(memorizingList) + return memorizingList } - public func getWord(by uuid: UUID) -> Single { + public func fetchWord(by uuid: UUID) -> Single { guard let word = _wordList.first(where: { $0.uuid == uuid }) else { return .error(WordUseCaseError.retrieveFailed(reason: .uuidInvaild(uuid: uuid))) } @@ -98,22 +98,17 @@ public final class WordUseCaseFake: WordUseCaseProtocol { return .just(()) } - public func shuffleUnmemorizedWordList() -> Single { - return getUnmemorizedWordList() - .doOnSuccess { wordList in - self._unmemorizedWordList.shuffle(with: wordList) - } - .mapToVoid() + public func shuffleUnmemorizedWordList() { + let unmemorizedWordList = fetchUnmemorizedWordList() + _unmemorizedWordList.shuffle(with: unmemorizedWordList) } - public func updateToNextWord() -> Single { + public func updateToNextWord() { _unmemorizedWordList.updateToNextWord() - return .just(()) } - public func updateToPreviousWord() -> Single { + public func updateToPreviousWord() { _unmemorizedWordList.updateToPreviousWord() - return .just(()) } public func markCurrentWordAsMemorized(uuid: UUID) -> Single { @@ -124,8 +119,8 @@ public final class WordUseCaseFake: WordUseCaseProtocol { return .just(()) } - public func getCurrentUnmemorizedWord() -> Infallible { - return .just(_unmemorizedWordList.getCurrentWord()) + public func getCurrentUnmemorizedWord() -> Domain.Word? { + return _unmemorizedWordList.getCurrentWord() } public func isWordDuplicated(_ word: String) -> Single { diff --git a/Sources/IOSScene/WordChecking/WordCheckingReactor.swift b/Sources/IOSScene/WordChecking/WordCheckingReactor.swift index fa8b041d..09c825e3 100644 --- a/Sources/IOSScene/WordChecking/WordCheckingReactor.swift +++ b/Sources/IOSScene/WordChecking/WordCheckingReactor.swift @@ -38,14 +38,14 @@ final class WordCheckingReactor: Reactor { case setCurrentWord(Word?) case setSourceLanguage(TranslationLanguage) case setTargetLanguage(TranslationLanguage) - case showAddCompleteToast(Result) + case showAddCompleteToast(Result) } struct State { var currentWord: Word? var translationSourceLanguage: TranslationLanguage var translationTargetLanguage: TranslationLanguage - @Pulse var showAddCompleteToast: Result? + @Pulse var showAddCompleteToast: Result? } let initialState: State = State( @@ -72,12 +72,10 @@ final class WordCheckingReactor: Reactor { func mutate(action: Action) -> Observable { switch action { case .viewDidLoad: - let initUnmemorizedWordList = wordUseCase.shuffleUnmemorizedWordList() - .asObservable() - .flatMap { self.wordUseCase.getCurrentUnmemorizedWord() } - .map(Mutation.setCurrentWord) - .catchAndReturn(.setCurrentWord(nil)) - .asObservable() + wordUseCase.shuffleUnmemorizedWordList() + let currentWord = wordUseCase.getCurrentUnmemorizedWord() + let initCurrrentWord: Mutation = .setCurrentWord(currentWord) + let initTranslationSourceLanguage = userSettingsUseCase.getCurrentTranslationLocale() .map(\.source) .map { Mutation.setSourceLanguage($0) } @@ -88,7 +86,7 @@ final class WordCheckingReactor: Reactor { .asObservable() return .merge([ - initUnmemorizedWordList, + .just(initCurrrentWord), initTranslationSourceLanguage, initTranslationTargetLanguage, ]) @@ -100,44 +98,36 @@ final class WordCheckingReactor: Reactor { return wordUseCase.addNewWord(newWord) .asObservable() - .flatMap { _ in self.wordUseCase.getCurrentUnmemorizedWord() } - .flatMap { currentWord -> Observable in + .flatMap { _ -> Observable in + let currentUnmemorizedWord = self.wordUseCase.getCurrentUnmemorizedWord() return .merge([ - .just(.setCurrentWord(currentWord)), - .just(.showAddCompleteToast(.success(newWord.word))), + .just(.setCurrentWord(currentUnmemorizedWord)), + .just(.showAddCompleteToast(.success(newWord))), ]) } .catch { error in switch error { case WordUseCaseError.saveFailed(reason: .duplicatedWord): return .just(.showAddCompleteToast(.failure(.addWordFailed(reason: .duplicatedWord(word: newWord.word))))) - case WordUseCaseError.noMemorizingWords: - return .just(.setCurrentWord(nil)) default: return .just(.showAddCompleteToast(.failure(.addWordFailed(reason: .unknown(word: newWord.word))))) } } case .updateToNextWord: - return wordUseCase.updateToNextWord() - .asObservable() - .flatMap { self.wordUseCase.getCurrentUnmemorizedWord() } - .map(Mutation.setCurrentWord) - .catchAndReturn(.setCurrentWord(nil)) + wordUseCase.updateToNextWord() + let currentWord = wordUseCase.getCurrentUnmemorizedWord() + return .just(Mutation.setCurrentWord(currentWord)) case .updateToPreviousWord: - return wordUseCase.updateToPreviousWord() - .asObservable() - .flatMap { self.wordUseCase.getCurrentUnmemorizedWord() } - .map(Mutation.setCurrentWord) - .catchAndReturn(.setCurrentWord(nil)) + wordUseCase.updateToPreviousWord() + let currentWord = wordUseCase.getCurrentUnmemorizedWord() + return .just(Mutation.setCurrentWord(currentWord)) case .shuffleWordList: - return wordUseCase.shuffleUnmemorizedWordList() - .asObservable() - .flatMap { self.wordUseCase.getCurrentUnmemorizedWord() } - .map(Mutation.setCurrentWord) - .catchAndReturn(.setCurrentWord(nil)) + wordUseCase.shuffleUnmemorizedWordList() + let currentWord = wordUseCase.getCurrentUnmemorizedWord() + return .just(Mutation.setCurrentWord(currentWord)) case .deleteCurrentWord: guard let uuid = currentState.currentWord?.uuid else { @@ -146,9 +136,10 @@ final class WordCheckingReactor: Reactor { return wordUseCase.deleteWord(by: uuid) .asObservable() - .flatMap { self.wordUseCase.getCurrentUnmemorizedWord() } - .map(Mutation.setCurrentWord) - .catchAndReturn(.setCurrentWord(nil)) + .map { _ -> Mutation in + let currentWord = self.wordUseCase.getCurrentUnmemorizedWord() + return .setCurrentWord(currentWord) + } case .markCurrentWordAsMemorized: guard let uuid = currentState.currentWord?.uuid else { @@ -157,9 +148,10 @@ final class WordCheckingReactor: Reactor { return wordUseCase.markCurrentWordAsMemorized(uuid: uuid) .asObservable() - .flatMap { self.wordUseCase.getCurrentUnmemorizedWord() } - .map(Mutation.setCurrentWord) - .catchAndReturn(.setCurrentWord(nil)) + .map { _ -> Mutation in + let currentWord = self.wordUseCase.getCurrentUnmemorizedWord() + return .setCurrentWord(currentWord) + } } } @@ -171,18 +163,26 @@ final class WordCheckingReactor: Reactor { globalAction.didSetTargetLanguage .map(Mutation.setTargetLanguage), globalAction.didEditWord - .flatMap { _ in return self.wordUseCase.getCurrentUnmemorizedWord() } - .map(Mutation.setCurrentWord), + .map { _ in + let currentWord = self.wordUseCase.getCurrentUnmemorizedWord() + return Mutation.setCurrentWord(currentWord) + }, globalAction.didDeleteWord .filter { $0.uuid == self.currentState.currentWord?.uuid } - .flatMap { _ in return self.wordUseCase.getCurrentUnmemorizedWord() } - .map(Mutation.setCurrentWord), + .map { _ in + let currentWord = self.wordUseCase.getCurrentUnmemorizedWord() + return Mutation.setCurrentWord(currentWord) + }, globalAction.didResetWordList - .flatMap { self.wordUseCase.getCurrentUnmemorizedWord() } - .map(Mutation.setCurrentWord), + .map { + let currentWord = self.wordUseCase.getCurrentUnmemorizedWord() + return Mutation.setCurrentWord(currentWord) + }, globalAction.didAddWord - .flatMap { self.wordUseCase.getCurrentUnmemorizedWord() } - .map(Mutation.setCurrentWord), + .map { + let currentWord = self.wordUseCase.getCurrentUnmemorizedWord() + return Mutation.setCurrentWord(currentWord) + }, ]) } diff --git a/Sources/IOSScene/WordChecking/WordCheckingViewController.swift b/Sources/IOSScene/WordChecking/WordCheckingViewController.swift index 453894dc..fede2f39 100644 --- a/Sources/IOSScene/WordChecking/WordCheckingViewController.swift +++ b/Sources/IOSScene/WordChecking/WordCheckingViewController.swift @@ -172,12 +172,13 @@ final class WordCheckingViewController: RxBaseViewController, View, WordChecking .disposed(by: self.disposeBag) reactor.pulse(\.$showAddCompleteToast) + // TODO: .unwrapOrIgnore() .asSignalOnErrorJustComplete() .emit(with: self) { owner, showAddCompleteToast in guard let showAddCompleteToast = showAddCompleteToast else { return } switch showAddCompleteToast { case .success(let word): - owner.view.makeToast(LocalizedString.word_added_successfully(word: word), duration: 2.0, position: .top) + owner.view.makeToast(LocalizedString.word_added_successfully(word: word.word), duration: 2.0, position: .top) case .failure(let error): switch error { case .addWordFailed(let reason): diff --git a/Sources/IOSScene/WordDetail/WordDetailReactor.swift b/Sources/IOSScene/WordDetail/WordDetailReactor.swift index dedf8c96..e4e2455a 100644 --- a/Sources/IOSScene/WordDetail/WordDetailReactor.swift +++ b/Sources/IOSScene/WordDetail/WordDetailReactor.swift @@ -79,7 +79,7 @@ final class WordDetailReactor: Reactor { func mutate(action: Action) -> Observable { switch action { case .viewDidLoad: - return wordUseCase.getWord(by: uuid) + return wordUseCase.fetchWord(by: uuid) .doOnSuccess { self.originWord = $0.word } diff --git a/Sources/IOSScene/WordList/WordListReactor.swift b/Sources/IOSScene/WordList/WordListReactor.swift index 8b6844bb..1203501d 100644 --- a/Sources/IOSScene/WordList/WordListReactor.swift +++ b/Sources/IOSScene/WordList/WordListReactor.swift @@ -100,20 +100,18 @@ final class WordListReactor: Reactor { /// /// 이 함수는 ListType 상태를 직접 업데이트 하지 않습니다. private func updateWordList(by listType: ListType) -> Observable { - let wordListSequence: Single<[Word]> + let wordList: [Word] switch listType { case .all: - wordListSequence = wordUseCase.getWordList() + wordList = wordUseCase.fetchWordList() case .memorized: - wordListSequence = wordUseCase.getMemorizedWordList() + wordList = wordUseCase.fetchMemorizedWordList() case .unmemorized: - wordListSequence = wordUseCase.getUnmemorizedWordList() + wordList = wordUseCase.fetchUnmemorizedWordList() } - return wordListSequence - .asObservable() - .map(Mutation.updateWordList) + return .just(Mutation.updateWordList(wordList)) } } diff --git a/Tests/DomainTests/WordUseCaseTests.swift b/Tests/DomainTests/WordUseCaseTests.swift index 122e85fb..86751556 100644 --- a/Tests/DomainTests/WordUseCaseTests.swift +++ b/Tests/DomainTests/WordUseCaseTests.swift @@ -67,7 +67,7 @@ final class WordUseCaseTests: XCTestCase { .single() // Assert - XCTAssertEqual(try sut.getWord(by: testUUID).toBlocking().single(), testWord) + XCTAssertEqual(try sut.fetchWord(by: testUUID).toBlocking().single(), testWord) XCTAssertEqual(notificationsUseCase.updateDailyReminderCallCount, 1) } @@ -83,8 +83,8 @@ final class WordUseCaseTests: XCTestCase { .single() // Assert - XCTAssertThrowsError(try sut.getWord(by: deleteTarget.uuid).toBlocking().single()) - XCTAssertFalse(try sut.getUnmemorizedWordList().toBlocking().single().contains(where: { $0.uuid == deleteTarget.uuid })) + XCTAssertThrowsError(try sut.fetchWord(by: deleteTarget.uuid).toBlocking().single()) + XCTAssertFalse(sut.fetchUnmemorizedWordList().contains(where: { $0.uuid == deleteTarget.uuid })) XCTAssertEqual(notificationsUseCase.updateDailyReminderCallCount, 1) } @@ -100,8 +100,8 @@ final class WordUseCaseTests: XCTestCase { .single() // Assert - XCTAssertThrowsError(try sut.getWord(by: deleteTarget.uuid).toBlocking().single()) - XCTAssertFalse(try sut.getMemorizedWordList().toBlocking().single().contains(where: { $0.uuid == deleteTarget.uuid })) + XCTAssertThrowsError(try sut.fetchWord(by: deleteTarget.uuid).toBlocking().single()) + XCTAssertFalse(sut.fetchMemorizedWordList().contains(where: { $0.uuid == deleteTarget.uuid })) XCTAssertEqual(notificationsUseCase.updateDailyReminderCallCount, 1) } @@ -110,9 +110,7 @@ final class WordUseCaseTests: XCTestCase { let preparedList = [unmemorizedWordList, memorizedWordList].flatMap { $0 } // Act - let wordList = try sut.getWordList() - .toBlocking() - .single() + let wordList = sut.fetchWordList() // Assert XCTAssertEqual(Set(wordList), Set(preparedList)) @@ -131,8 +129,8 @@ final class WordUseCaseTests: XCTestCase { .single() // Assert - XCTAssertEqual(try sut.getWord(by: updateTarget.uuid).toBlocking().single().word, "UpdatedWord") - XCTAssertEqual(try sut.getWord(by: updateTarget.uuid).toBlocking().single().memorizedState, .memorizing) + XCTAssertEqual(try sut.fetchWord(by: updateTarget.uuid).toBlocking().single().word, "UpdatedWord") + XCTAssertEqual(try sut.fetchWord(by: updateTarget.uuid).toBlocking().single().memorizedState, .memorizing) } func test_updateUnmemorizedWordToMemorized() throws { @@ -148,7 +146,7 @@ final class WordUseCaseTests: XCTestCase { .single() // Assert - XCTAssertEqual(try sut.getWord(by: updateTarget.uuid).toBlocking().single().memorizedState, .memorized) + XCTAssertEqual(try sut.fetchWord(by: updateTarget.uuid).toBlocking().single().memorizedState, .memorized) } func test_updateMemorizedWordToUnmemorized() throws { @@ -164,7 +162,7 @@ final class WordUseCaseTests: XCTestCase { .single() // Assert - XCTAssertEqual(try sut.getWord(by: updateTarget.uuid).toBlocking().single().memorizedState, .memorizing) + XCTAssertEqual(try sut.fetchWord(by: updateTarget.uuid).toBlocking().single().memorizedState, .memorizing) } func test_shuffleUnmemorizedWordListWhenOnly1Element() throws { @@ -178,27 +176,21 @@ final class WordUseCaseTests: XCTestCase { } // Act - try sut.shuffleUnmemorizedWordList() - .toBlocking() - .single() + sut.shuffleUnmemorizedWordList() // Assert - XCTAssertEqual(try sut.getCurrentUnmemorizedWord().toBlocking().single(), testWord) + XCTAssertEqual(sut.getCurrentUnmemorizedWord(), testWord) } func test_shuffleUnmemorizedWordListWhenMoreThen2Element() throws { // Arrange - let oldCurrentWord = try sut.getCurrentUnmemorizedWord() - .toBlocking() - .single() + let oldCurrentWord = sut.getCurrentUnmemorizedWord() // Act - try sut.shuffleUnmemorizedWordList() - .toBlocking() - .single() + sut.shuffleUnmemorizedWordList() // Assert - XCTAssertNotEqual(try sut.getCurrentUnmemorizedWord().toBlocking().single(), oldCurrentWord) + XCTAssertNotEqual(sut.getCurrentUnmemorizedWord(), oldCurrentWord) } func test_addDuplicatedWord() throws { diff --git a/Tests/WordCheckerUITests/WordCheckerUITests.swift b/Tests/WordCheckerUITests/WordCheckerUITests.swift index 3c41121a..fbbbf57c 100644 --- a/Tests/WordCheckerUITests/WordCheckerUITests.swift +++ b/Tests/WordCheckerUITests/WordCheckerUITests.swift @@ -18,6 +18,7 @@ @testable import IPhoneDriver import XCTest +typealias DomainString = Domain.LocalizedString typealias WordCheckingString = IOSScene_WordChecking.LocalizedString typealias WordCheckingAccessibilityID = IOSScene_WordChecking.AccessibilityIdentifier typealias WordListString = IOSScene_WordList.LocalizedString From 8db3c333c539dcfe28137f6d4e3394edc6f23cb2 Mon Sep 17 00:00:00 2001 From: Jaewon Yun Date: Wed, 28 Feb 2024 16:04:05 +0900 Subject: [PATCH 04/11] Update lint rules --- .swiftlint.yml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/.swiftlint.yml b/.swiftlint.yml index a4fb632e..2ccfd562 100644 --- a/.swiftlint.yml +++ b/.swiftlint.yml @@ -4,11 +4,7 @@ disabled_rules: - type_name - function_body_length -trailing_whitespace: - severity: error - trailing_comma: - severity: error mandatory_comma: true included: From 6f3ffab9072daf28ba9bbac0450e1318dd73e57b Mon Sep 17 00:00:00 2001 From: Jaewon Yun Date: Wed, 28 Feb 2024 16:33:00 +0900 Subject: [PATCH 05/11] Apply framework name changes --- Project.swift | 6 +++--- Sources/Domain/UseCases/ExternalStoreUseCase.swift | 2 +- Sources/Domain/UseCases/NotificationsUseCase.swift | 2 +- Sources/Domain/UseCases/UserSettingsUseCase.swift | 2 +- Sources/DomainTesting/WordUseCaseFake.swift | 2 +- .../GeneralSettings/GeneralSettingsViewController.swift | 2 +- .../IOSScene/UserSettings/UserSettingsViewController.swift | 2 +- .../IOSScene/WordAddition/WordAdditionViewController.swift | 2 +- Sources/IOSScene/WordAddition/WordAdditionViewModel.swift | 2 +- .../IOSScene/WordChecking/WordCheckingViewController.swift | 2 +- Sources/IOSScene/WordList/WordListViewController.swift | 2 +- .../Infrastructure/DomainImpl/UserSettingsRepository.swift | 2 +- .../UIPresentationController+didAttemptToDismiss.swift | 2 +- Tuist/Dependencies.swift | 4 ++-- .../ProjectDescriptionHelpers/ExternalDependencyName.swift | 2 +- 15 files changed, 18 insertions(+), 18 deletions(-) diff --git a/Project.swift b/Project.swift index 06af0aa1..295d74dd 100644 --- a/Project.swift +++ b/Project.swift @@ -20,7 +20,7 @@ func targets() -> [Target] { dependencies: [ .target(name: "Utility"), .external(name: ExternalDependencyName.rxSwift), - .external(name: ExternalDependencyName.rxUtilityDynamic), + .external(name: ExternalDependencyName.rxSwiftSugarDynamic), .external(name: ExternalDependencyName.swinject), .external(name: ExternalDependencyName.swinjectExtension), .external(name: ExternalDependencyName.foundationPlus), @@ -66,7 +66,7 @@ func targets() -> [Target] { .package(product: ExternalDependencyName.googleAPIClientForREST_Drive), .package(product: ExternalDependencyName.googleSignIn), .external(name: ExternalDependencyName.rxSwift), - .external(name: ExternalDependencyName.rxUtilityDynamic), + .external(name: ExternalDependencyName.rxSwiftSugarDynamic), .external(name: ExternalDependencyName.extendedUserDefaultsRxExtension), .external(name: ExternalDependencyName.swinject), .external(name: ExternalDependencyName.swinjectExtension), @@ -92,7 +92,7 @@ func targets() -> [Target] { .target(name: "Utility"), .external(name: ExternalDependencyName.rxSwift), .external(name: ExternalDependencyName.rxCocoa), - .external(name: ExternalDependencyName.rxUtilityDynamic), + .external(name: ExternalDependencyName.rxSwiftSugarDynamic), .external(name: ExternalDependencyName.reactorKit), .external(name: ExternalDependencyName.snapKit), .external(name: ExternalDependencyName.then), diff --git a/Sources/Domain/UseCases/ExternalStoreUseCase.swift b/Sources/Domain/UseCases/ExternalStoreUseCase.swift index 7cef9ebd..e00965a8 100644 --- a/Sources/Domain/UseCases/ExternalStoreUseCase.swift +++ b/Sources/Domain/UseCases/ExternalStoreUseCase.swift @@ -8,7 +8,7 @@ import Foundation import RxSwift -import RxUtility +import RxSwiftSugar public final class ExternalStoreUseCase: ExternalStoreUseCaseProtocol { diff --git a/Sources/Domain/UseCases/NotificationsUseCase.swift b/Sources/Domain/UseCases/NotificationsUseCase.swift index 52679471..5d40701b 100644 --- a/Sources/Domain/UseCases/NotificationsUseCase.swift +++ b/Sources/Domain/UseCases/NotificationsUseCase.swift @@ -9,7 +9,7 @@ import Foundation import FoundationPlus import RxSwift -import RxUtility +import RxSwiftSugar import UserNotifications final class NotificationsUseCase: NotificationsUseCaseProtocol { diff --git a/Sources/Domain/UseCases/UserSettingsUseCase.swift b/Sources/Domain/UseCases/UserSettingsUseCase.swift index 1075d24f..a29505c4 100644 --- a/Sources/Domain/UseCases/UserSettingsUseCase.swift +++ b/Sources/Domain/UseCases/UserSettingsUseCase.swift @@ -9,7 +9,7 @@ import Foundation import RxSwift import RxRelay -import RxUtility +import RxSwiftSugar import Utility public final class UserSettingsUseCase: UserSettingsUseCaseProtocol { diff --git a/Sources/DomainTesting/WordUseCaseFake.swift b/Sources/DomainTesting/WordUseCaseFake.swift index 7521712a..da452fd8 100644 --- a/Sources/DomainTesting/WordUseCaseFake.swift +++ b/Sources/DomainTesting/WordUseCaseFake.swift @@ -10,7 +10,7 @@ import Foundation import InfrastructureTesting import RxSwift -import RxUtility +import RxSwiftSugar import Utility public final class WordUseCaseFake: WordUseCaseProtocol { diff --git a/Sources/IOSScene/GeneralSettings/GeneralSettingsViewController.swift b/Sources/IOSScene/GeneralSettings/GeneralSettingsViewController.swift index 2f1b4bf7..9bf2ed3a 100644 --- a/Sources/IOSScene/GeneralSettings/GeneralSettingsViewController.swift +++ b/Sources/IOSScene/GeneralSettings/GeneralSettingsViewController.swift @@ -8,7 +8,7 @@ import IOSSupport import ReactorKit -import RxUtility +import RxSwiftSugar import Then import UIKit import UIKitPlus diff --git a/Sources/IOSScene/UserSettings/UserSettingsViewController.swift b/Sources/IOSScene/UserSettings/UserSettingsViewController.swift index c2b877e7..735b4a6f 100644 --- a/Sources/IOSScene/UserSettings/UserSettingsViewController.swift +++ b/Sources/IOSScene/UserSettings/UserSettingsViewController.swift @@ -10,7 +10,7 @@ import Domain import IOSSupport import ReactorKit import RxSwift -import RxUtility +import RxSwiftSugar import Then import UIKit diff --git a/Sources/IOSScene/WordAddition/WordAdditionViewController.swift b/Sources/IOSScene/WordAddition/WordAdditionViewController.swift index 1468b436..7a400fe7 100644 --- a/Sources/IOSScene/WordAddition/WordAdditionViewController.swift +++ b/Sources/IOSScene/WordAddition/WordAdditionViewController.swift @@ -8,7 +8,7 @@ import IOSSupport import RxSwift import RxCocoa -import RxUtility +import RxSwiftSugar import SnapKit import Then import UIKit diff --git a/Sources/IOSScene/WordAddition/WordAdditionViewModel.swift b/Sources/IOSScene/WordAddition/WordAdditionViewModel.swift index 05b53fab..cd816771 100644 --- a/Sources/IOSScene/WordAddition/WordAdditionViewModel.swift +++ b/Sources/IOSScene/WordAddition/WordAdditionViewModel.swift @@ -11,7 +11,7 @@ import FoundationPlus import IOSSupport import RxSwift import RxCocoa -import RxUtility +import RxSwiftSugar final class WordAdditionViewModel: ViewModelType { diff --git a/Sources/IOSScene/WordChecking/WordCheckingViewController.swift b/Sources/IOSScene/WordChecking/WordCheckingViewController.swift index fede2f39..10d54b65 100644 --- a/Sources/IOSScene/WordChecking/WordCheckingViewController.swift +++ b/Sources/IOSScene/WordChecking/WordCheckingViewController.swift @@ -9,7 +9,7 @@ import IOSSupport import ReactorKit import RxSwift import RxCocoa -import RxUtility +import RxSwiftSugar import SFSafeSymbols import Then import Toast diff --git a/Sources/IOSScene/WordList/WordListViewController.swift b/Sources/IOSScene/WordList/WordListViewController.swift index 4f00e1c4..7c4f50a1 100644 --- a/Sources/IOSScene/WordList/WordListViewController.swift +++ b/Sources/IOSScene/WordList/WordListViewController.swift @@ -7,7 +7,7 @@ import IOSSupport import ReactorKit -import RxUtility +import RxSwiftSugar import SFSafeSymbols import SnapKit import Then diff --git a/Sources/Infrastructure/DomainImpl/UserSettingsRepository.swift b/Sources/Infrastructure/DomainImpl/UserSettingsRepository.swift index 8c78072c..2d12335c 100644 --- a/Sources/Infrastructure/DomainImpl/UserSettingsRepository.swift +++ b/Sources/Infrastructure/DomainImpl/UserSettingsRepository.swift @@ -11,7 +11,7 @@ import ExtendedUserDefaults import ExtendedUserDefaultsRxExtension import Foundation import RxSwift -import RxUtility +import RxSwiftSugar final class UserSettingsRepository: UserSettingsRepositoryProtocol { diff --git a/Sources/iOSSupport/RxDelegates/UIPresentationController+didAttemptToDismiss.swift b/Sources/iOSSupport/RxDelegates/UIPresentationController+didAttemptToDismiss.swift index 3f48908a..d734d76c 100644 --- a/Sources/iOSSupport/RxDelegates/UIPresentationController+didAttemptToDismiss.swift +++ b/Sources/iOSSupport/RxDelegates/UIPresentationController+didAttemptToDismiss.swift @@ -8,7 +8,7 @@ import UIKit import RxSwift import RxCocoa -import RxUtility +import RxSwiftSugar extension Reactive where Base: UIPresentationController { diff --git a/Tuist/Dependencies.swift b/Tuist/Dependencies.swift index d55e5823..098fd4f2 100644 --- a/Tuist/Dependencies.swift +++ b/Tuist/Dependencies.swift @@ -19,8 +19,8 @@ let dependencies = Dependencies( // Realm .remote(url: "https://github.com/realm/realm-swift.git", requirement: .upToNextMajor(from: "10.42.0")), - // RxUtility - .remote(url: "https://github.com/Woin2ee-Modules/RxUtility.git", + // RxSwiftSugar + .remote(url: "https://github.com/Woin2ee-Modules/RxSwiftSugar.git", requirement: .upToNextMajor(from: "1.0.0")), // ExtendedUserDefaults .remote(url: "https://github.com/Woin2ee-Modules/ExtendedUserDefaults.git", diff --git a/Tuist/ProjectDescriptionHelpers/ExternalDependencyName.swift b/Tuist/ProjectDescriptionHelpers/ExternalDependencyName.swift index 5eb258e9..39a491f0 100644 --- a/Tuist/ProjectDescriptionHelpers/ExternalDependencyName.swift +++ b/Tuist/ProjectDescriptionHelpers/ExternalDependencyName.swift @@ -12,7 +12,7 @@ public struct ExternalDependencyName { public static let snapKit = "SnapKit" public static let realm = "Realm" public static let realmSwift = "RealmSwift" - public static let rxUtilityDynamic = "RxUtility-Dynamic" + public static let rxSwiftSugarDynamic = "RxSwiftSugar-Dynamic" public static let extendedUserDefaults = "ExtendedUserDefaults" public static let extendedUserDefaultsRxExtension = "ExtendedUserDefaultsRxExtension" public static let sfSafeSymbols = "SFSafeSymbols" From 964f93b1bf587a00510a2ca4f3ea9935ce93fb2c Mon Sep 17 00:00:00 2001 From: Jaewon Yun Date: Wed, 28 Feb 2024 18:51:42 +0900 Subject: [PATCH 06/11] Add syntax sugars --- Sources/IOSScene/WordAddition/WordAdditionViewModel.swift | 2 +- Sources/IOSScene/WordChecking/WordCheckingViewController.swift | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/Sources/IOSScene/WordAddition/WordAdditionViewModel.swift b/Sources/IOSScene/WordAddition/WordAdditionViewModel.swift index cd816771..3531145e 100644 --- a/Sources/IOSScene/WordAddition/WordAdditionViewModel.swift +++ b/Sources/IOSScene/WordAddition/WordAdditionViewModel.swift @@ -31,7 +31,7 @@ final class WordAdditionViewModel: ViewModelType { .map { wordText -> Word? in return try? .init(word: wordText) } - .compactMap { $0 } + .unwrapOrIgnore() .flatMapFirst { newWord in return self.wordUseCase.addNewWord(newWord) .asSignalOnErrorJustComplete() diff --git a/Sources/IOSScene/WordChecking/WordCheckingViewController.swift b/Sources/IOSScene/WordChecking/WordCheckingViewController.swift index 10d54b65..06327f5c 100644 --- a/Sources/IOSScene/WordChecking/WordCheckingViewController.swift +++ b/Sources/IOSScene/WordChecking/WordCheckingViewController.swift @@ -172,10 +172,9 @@ final class WordCheckingViewController: RxBaseViewController, View, WordChecking .disposed(by: self.disposeBag) reactor.pulse(\.$showAddCompleteToast) - // TODO: .unwrapOrIgnore() + .unwrapOrIgnore() .asSignalOnErrorJustComplete() .emit(with: self) { owner, showAddCompleteToast in - guard let showAddCompleteToast = showAddCompleteToast else { return } switch showAddCompleteToast { case .success(let word): owner.view.makeToast(LocalizedString.word_added_successfully(word: word.word), duration: 2.0, position: .top) From 166cd721c64e5f2dde2443296d7c2383bb0c0a7d Mon Sep 17 00:00:00 2001 From: Jaewon Yun Date: Wed, 28 Feb 2024 22:42:11 +0900 Subject: [PATCH 07/11] Set deployment target for spm dependencies --- Tuist/Dependencies.swift | 101 +++++++++++++++++++++------------------ 1 file changed, 54 insertions(+), 47 deletions(-) diff --git a/Tuist/Dependencies.swift b/Tuist/Dependencies.swift index 098fd4f2..c8b7826b 100644 --- a/Tuist/Dependencies.swift +++ b/Tuist/Dependencies.swift @@ -3,52 +3,59 @@ import ProjectDescription let dependencies = Dependencies( carthage: [ ], - swiftPackageManager: [ - // RxSwift - .remote(url: "https://github.com/ReactiveX/RxSwift.git", - requirement: .upToNextMajor(from: "6.6.0")), - // Swinject - .remote(url: "https://github.com/Swinject/Swinject.git", - requirement: .upToNextMajor(from: "2.8.0")), - // SwinjectExtension - .remote(url: "https://github.com/Woin2ee-Modules/SwinjectExtension.git", - requirement: .upToNextMajor(from: "2.0.0")), - // SnapKit - .remote(url: "https://github.com/SnapKit/SnapKit.git", - requirement: .upToNextMajor(from: "5.0.1")), - // Realm - .remote(url: "https://github.com/realm/realm-swift.git", - requirement: .upToNextMajor(from: "10.42.0")), - // RxSwiftSugar - .remote(url: "https://github.com/Woin2ee-Modules/RxSwiftSugar.git", - requirement: .upToNextMajor(from: "1.0.0")), - // ExtendedUserDefaults - .remote(url: "https://github.com/Woin2ee-Modules/ExtendedUserDefaults.git", - requirement: .upToNextMajor(from: "1.0.0")), - // SFSafeSymbols - .remote(url: "https://github.com/SFSafeSymbols/SFSafeSymbols.git", - requirement: .upToNextMajor(from: "4.0.0")), - // Then - .remote(url: "https://github.com/devxoul/Then", - requirement: .upToNextMajor(from: "3.0.0")), - // Toast-Swift - .remote(url: "https://github.com/scalessec/Toast-Swift.git", - requirement: .upToNextMajor(from: "5.0.0")), - // GoogleSignIn-iOS - .remote(url: "https://github.com/google/GoogleSignIn-iOS", - requirement: .upToNextMajor(from: "6.0.2")), - // google-api-objectivec-client-for-rest - .remote(url: "https://github.com/google/google-api-objectivec-client-for-rest.git", - requirement: .upToNextMajor(from: "3.0.0")), - // ReactorKit - .remote(url: "https://github.com/ReactorKit/ReactorKit.git", - requirement: .upToNextMajor(from: "3.0.0")), - // FoundationPlus - .remote(url: "https://github.com/Woin2ee-Modules/FoundationPlus.git", - requirement: .upToNextMajor(from: "1.0.0")), - // UIKitPlus - .remote(url: "https://github.com/Woin2ee-Modules/UIKitPlus.git", - requirement: .upToNextMajor(from: "1.0.0")), - ], + swiftPackageManager: SwiftPackageManagerDependencies( + [ + // RxSwift + .remote(url: "https://github.com/ReactiveX/RxSwift.git", + requirement: .upToNextMajor(from: "6.6.0")), + // Swinject + .remote(url: "https://github.com/Swinject/Swinject.git", + requirement: .upToNextMajor(from: "2.8.0")), + // SwinjectExtension + .remote(url: "https://github.com/Woin2ee-Modules/SwinjectExtension.git", + requirement: .upToNextMajor(from: "2.0.0")), + // SnapKit + .remote(url: "https://github.com/SnapKit/SnapKit.git", + requirement: .upToNextMajor(from: "5.0.1")), + // Realm + .remote(url: "https://github.com/realm/realm-swift.git", + requirement: .upToNextMajor(from: "10.42.0")), + // RxSwiftSugar + .remote(url: "https://github.com/Woin2ee-Modules/RxSwiftSugar.git", + requirement: .upToNextMajor(from: "1.0.0")), + // ExtendedUserDefaults + .remote(url: "https://github.com/Woin2ee-Modules/ExtendedUserDefaults.git", + requirement: .upToNextMajor(from: "1.0.0")), + // SFSafeSymbols + .remote(url: "https://github.com/SFSafeSymbols/SFSafeSymbols.git", + requirement: .upToNextMajor(from: "4.0.0")), + // Then + .remote(url: "https://github.com/devxoul/Then", + requirement: .upToNextMajor(from: "3.0.0")), + // Toast-Swift + .remote(url: "https://github.com/scalessec/Toast-Swift.git", + requirement: .upToNextMajor(from: "5.0.0")), + // GoogleSignIn-iOS + .remote(url: "https://github.com/google/GoogleSignIn-iOS", + requirement: .upToNextMajor(from: "6.0.2")), + // google-api-objectivec-client-for-rest + .remote(url: "https://github.com/google/google-api-objectivec-client-for-rest.git", + requirement: .upToNextMajor(from: "3.0.0")), + // ReactorKit + .remote(url: "https://github.com/ReactorKit/ReactorKit.git", + requirement: .upToNextMajor(from: "3.0.0")), + // FoundationPlus + .remote(url: "https://github.com/Woin2ee-Modules/FoundationPlus.git", + requirement: .upToNextMajor(from: "1.0.0")), + // UIKitPlus + .remote(url: "https://github.com/Woin2ee-Modules/UIKitPlus.git", + requirement: .upToNextMajor(from: "1.0.0")), + ], + baseSettings: .settings( + base: [ + "IPHONEOS_DEPLOYMENT_TARGET": "12.0" + ] + ) + ), platforms: [.iOS] ) From 0c8b3fab5c035ec76b105d1989a38826c2f5e82d Mon Sep 17 00:00:00 2001 From: Jaewon Yun Date: Thu, 29 Feb 2024 15:32:19 +0900 Subject: [PATCH 08/11] Add Logger to Domain --- Sources/Domain/Common/DomainLogger.swift | 24 +++++++++++++++++++ .../LocalizedString.swift | 0 Sources/Domain/Entities/Word.swift | 1 + .../UseCases/NotificationsUseCase.swift | 2 +- 4 files changed, 26 insertions(+), 1 deletion(-) create mode 100644 Sources/Domain/Common/DomainLogger.swift rename Sources/Domain/{Localization => Common}/LocalizedString.swift (100%) diff --git a/Sources/Domain/Common/DomainLogger.swift b/Sources/Domain/Common/DomainLogger.swift new file mode 100644 index 00000000..7c7aa55e --- /dev/null +++ b/Sources/Domain/Common/DomainLogger.swift @@ -0,0 +1,24 @@ +// +// DomainLogger.swift +// Domain +// +// Created by Jaewon Yun on 2/29/24. +// Copyright © 2024 woin2ee. All rights reserved. +// + +import Foundation +import OSLog + +internal typealias DomainLogger = Logger + +internal extension DomainLogger { + + enum Category: String { + case entity + case useCase + } + + init(category: Category) { + self.init(subsystem: "Domain", category: category.rawValue.capitalized) + } +} diff --git a/Sources/Domain/Localization/LocalizedString.swift b/Sources/Domain/Common/LocalizedString.swift similarity index 100% rename from Sources/Domain/Localization/LocalizedString.swift rename to Sources/Domain/Common/LocalizedString.swift diff --git a/Sources/Domain/Entities/Word.swift b/Sources/Domain/Entities/Word.swift index 8d51c69c..eaf72bbb 100644 --- a/Sources/Domain/Entities/Word.swift +++ b/Sources/Domain/Entities/Word.swift @@ -21,6 +21,7 @@ public final class Word: Codable { public init(uuid: UUID = .init(), word: String, memorizedState: MemorizedState = .memorizing) throws { guard word.hasElements else { + DomainLogger(category: .entity).error("Attempted to create a `Word` entity with an empty word.") throw EntityError.changeRejected(reason: .valueDisallowed) } diff --git a/Sources/Domain/UseCases/NotificationsUseCase.swift b/Sources/Domain/UseCases/NotificationsUseCase.swift index 5d40701b..99587c8f 100644 --- a/Sources/Domain/UseCases/NotificationsUseCase.swift +++ b/Sources/Domain/UseCases/NotificationsUseCase.swift @@ -76,7 +76,7 @@ final class NotificationsUseCase: NotificationsUseCaseProtocol { do { try self.localNotificationService.saveLatestDailyReminderTime(time) } catch { - // TODO: 예외 상황 로그 추가 + DomainLogger(category: .useCase).error("Not saved latest daily reminder time(\(time)). \(error)") } Task { From fc69f233000ab0420528cbdc0f32708a75471c1b Mon Sep 17 00:00:00 2001 From: Jaewon Yun Date: Fri, 1 Mar 2024 00:02:43 +0900 Subject: [PATCH 09/11] Update tuist version to 4.4.0 --- .gitignore | 11 +- .mise.toml | 2 + Project.swift | 289 +++++++++--------- Tuist/Dependencies.swift | 61 ---- Tuist/Package.resolved | 185 +++++++++++ Tuist/Package.swift | 52 ++++ .../Configurations.swift | 11 +- .../DeploymentTarget+platform.swift | 29 -- .../Extensions/Target+module.swift | 185 ++++++++--- .../Scheme+Templates.swift | 16 - 10 files changed, 541 insertions(+), 300 deletions(-) create mode 100644 .mise.toml delete mode 100644 Tuist/Dependencies.swift create mode 100644 Tuist/Package.resolved create mode 100644 Tuist/Package.swift delete mode 100644 Tuist/ProjectDescriptionHelpers/Extensions/DeploymentTarget+platform.swift delete mode 100644 Tuist/ProjectDescriptionHelpers/Scheme+Templates.swift diff --git a/.gitignore b/.gitignore index 46ac960c..fa0f5179 100644 --- a/.gitignore +++ b/.gitignore @@ -67,7 +67,8 @@ graph.dot Derived/ ### Tuist managed dependencies ### -Tuist/Dependencies +Tuist/Dependencies/graph.json +Tuist/Dependencies/SwiftPackageManager ### Tuist Signing Tuist/Signing @@ -84,3 +85,11 @@ Changelog/next.md ### Personal Playground MyPlayground.playground + +# Swift Package Manager +# +# Add this line if you want to avoid checking in source code from Swift Package Manager dependencies. +# Packages/ +# Package.pins +.build/ +.swiftpm \ No newline at end of file diff --git a/.mise.toml b/.mise.toml new file mode 100644 index 00000000..2b87b704 --- /dev/null +++ b/.mise.toml @@ -0,0 +1,2 @@ +[tools] +tuist = "4.4.0" diff --git a/Project.swift b/Project.swift index 295d74dd..98be1838 100644 --- a/Project.swift +++ b/Project.swift @@ -5,26 +5,24 @@ import MyPlugin // Local plugin loaded let localHelper = LocalHelper(name: "MyPlugin") -// MARK: - Targets - var schemes: [Scheme] = [] var disposedSchemes: [Scheme] = [] -// swiftlint:disable:next function_body_length -func targets() -> [Target] { - let targets = +// MARK: - Targets + +func commonTargets() -> [Target] { [ - Target.module( + Target.makeCommonFramework( name: "Domain", - resourceOptions: [.own], dependencies: [ .target(name: "Utility"), - .external(name: ExternalDependencyName.rxSwift), + .external(name: ExternalDependencyName.rxSwift, condition: nil), .external(name: ExternalDependencyName.rxSwiftSugarDynamic), .external(name: ExternalDependencyName.swinject), .external(name: ExternalDependencyName.swinjectExtension), .external(name: ExternalDependencyName.foundationPlus), ], + resourceOptions: [.own], hasTests: true, additionalTestDependencies: [ .target(name: "TestsSupport"), @@ -33,16 +31,16 @@ func targets() -> [Target] { .external(name: ExternalDependencyName.rxBlocking), ], appendSchemeTo: &schemes - ) - + Target.module( + ), + Target.makeCommonFramework( name: "DomainTesting", dependencies: [ .target(name: "Domain"), .target(name: "InfrastructureTesting"), ], appendSchemeTo: &disposedSchemes - ) - + Target.module( + ), + Target.makeCommonFramework( name: "Utility", scripts: [ // 공동 작업자의 githook path 자동 세팅을 위함 @@ -54,8 +52,8 @@ func targets() -> [Target] { ], hasTests: true, appendSchemeTo: &schemes - ) - + Target.module( + ), + Target.makeCommonFramework( name: "Infrastructure", dependencies: [ .target(name: "Domain"), @@ -76,17 +74,50 @@ func targets() -> [Target] { .external(name: ExternalDependencyName.rxBlocking), ], appendSchemeTo: &schemes - ) - + Target.module( + ), + Target.makeCommonFramework( name: "InfrastructureTesting", dependencies: [ .target(name: "Domain"), ], appendSchemeTo: &disposedSchemes - ) - + Target.module( + ), + Target.makeCommonFramework( + name: "TestsSupport", + dependencies: [ + .target(name: "Domain"), + .external(name: ExternalDependencyName.rxSwift), + .external(name: ExternalDependencyName.rxTest), + ], + settings: .settings(base: ["ENABLE_TESTING_SEARCH_PATHS": "YES"]), + appendSchemeTo: &disposedSchemes + ), + ] + .flatMap { $0 } +} + +func iOSTargets() -> [Target] { + [ + Target.target( + name: "\(PROJECT_NAME)UITests", + destinations: [.iPhone], + product: .uiTests, + bundleId: "\(BASIC_BUNDLE_ID)UITests", + deploymentTargets: .iOS(MINIMUM_IOS_VERSION), + sources: "Tests/\(PROJECT_NAME)UITests/**", + dependencies: [ + .target(name: "\(PROJECT_NAME)Dev"), + .target(name: "IOSSupport"), + .target(name: "Utility"), + .package(product: "Realm"), + .package(product: ExternalDependencyName.googleAPIClientForRESTCore), + .package(product: ExternalDependencyName.googleAPIClientForREST_Drive), + .package(product: ExternalDependencyName.googleSignIn), + ] + ), + ] + [ + Target.makeIOSFramework( name: "IOSSupport", - resourceOptions: [.own], dependencies: [ .target(name: "Domain"), .target(name: "Utility"), @@ -103,14 +134,13 @@ func targets() -> [Target] { .external(name: ExternalDependencyName.uiKitPlus), .package(product: ExternalDependencyName.swiftCollections), ], + resourceOptions: [.own], appendSchemeTo: &schemes - ) - + Target.module( + ), + Target.makeIOSFramework( name: "IOSScene_WordChecking", + dependencies: [.target(name: "IOSSupport"),], resourceOptions: [.own], - dependencies: [ - .target(name: "IOSSupport"), - ], hasTests: true, additionalTestDependencies: [ .target(name: "DomainTesting"), @@ -118,23 +148,23 @@ func targets() -> [Target] { .external(name: ExternalDependencyName.rxTest), ], appendSchemeTo: &schemes - ) - + Target.module( + ), + Target.makeTargets( name: "IOSScene_WordCheckingExample", + destinations: .iOS, product: .app, + deploymentTargets: .iOS(MINIMUM_IOS_VERSION), infoPlist: .file(path: "Resources/InfoPlist/InfoExample.plist"), dependencies: [ .target(name: "IOSScene_WordChecking"), .target(name: "DomainTesting"), ], appendSchemeTo: &schemes - ) - + Target.module( + ), + Target.makeIOSFramework( name: "IOSScene_WordList", + dependencies: [.target(name: "IOSSupport"),], resourceOptions: [.own], - dependencies: [ - .target(name: "IOSSupport"), - ], hasTests: true, additionalTestDependencies: [ .target(name: "DomainTesting"), @@ -142,13 +172,11 @@ func targets() -> [Target] { .external(name: ExternalDependencyName.rxTest), ], appendSchemeTo: &schemes - ) - + Target.module( + ), + Target.makeIOSFramework( name: "IOSScene_WordDetail", + dependencies: [.target(name: "IOSSupport"),], resourceOptions: [.own], - dependencies: [ - .target(name: "IOSSupport"), - ], hasTests: true, additionalTestDependencies: [ .target(name: "DomainTesting"), @@ -156,13 +184,11 @@ func targets() -> [Target] { .external(name: ExternalDependencyName.rxTest), ], appendSchemeTo: &schemes - ) - + Target.module( + ), + Target.makeIOSFramework( name: "IOSScene_WordAddition", + dependencies: [.target(name: "IOSSupport"),], resourceOptions: [.own], - dependencies: [ - .target(name: "IOSSupport"), - ], hasTests: true, additionalTestDependencies: [ .target(name: "DomainTesting"), @@ -170,13 +196,11 @@ func targets() -> [Target] { .external(name: ExternalDependencyName.rxTest), ], appendSchemeTo: &schemes - ) - + Target.module( + ), + Target.makeIOSFramework( name: "IOSScene_UserSettings", + dependencies: [.target(name: "IOSSupport"),], resourceOptions: [.own], - dependencies: [ - .target(name: "IOSSupport"), - ], hasTests: true, additionalTestDependencies: [ .target(name: "DomainTesting"), @@ -185,23 +209,23 @@ func targets() -> [Target] { .external(name: ExternalDependencyName.rxTest), ], appendSchemeTo: &schemes - ) - + Target.module( + ), + Target.makeTargets( name: "IOSScene_UserSettingsExample", + destinations: .iOS, product: .app, + deploymentTargets: .iOS(MINIMUM_IOS_VERSION), infoPlist: .file(path: "Resources/InfoPlist/InfoExample.plist"), dependencies: [ .target(name: "IOSScene_UserSettings"), .target(name: "DomainTesting"), ], appendSchemeTo: &schemes - ) - + Target.module( + ), + Target.makeIOSFramework( name: "IOSScene_LanguageSetting", + dependencies: [.target(name: "IOSSupport"),], resourceOptions: [.own], - dependencies: [ - .target(name: "IOSSupport"), - ], hasTests: true, additionalTestDependencies: [ .target(name: "DomainTesting"), @@ -209,59 +233,58 @@ func targets() -> [Target] { .external(name: ExternalDependencyName.rxTest), ], appendSchemeTo: &schemes - ) - + Target.module( + ), + Target.makeIOSFramework( name: "IOSScene_ThemeSetting", + dependencies: [.target(name: "IOSSupport"),], resourceOptions: [.own], - dependencies: [ - .target(name: "IOSSupport"), - ], hasTests: true, additionalTestDependencies: [ .target(name: "DomainTesting"), .external(name: ExternalDependencyName.rxBlocking), + .external(name: ExternalDependencyName.rxTest), ], appendSchemeTo: &schemes - ) - + Target.module( + ), + Target.makeIOSFramework( name: "IOSScene_PushNotificationSettings", + dependencies: [.target(name: "IOSSupport"),], resourceOptions: [.own], - dependencies: [ - .target(name: "IOSSupport"), - ], hasTests: true, additionalTestDependencies: [ .target(name: "DomainTesting"), .external(name: ExternalDependencyName.rxBlocking), ], appendSchemeTo: &schemes - ) - + Target.module( + ), + Target.makeTargets( name: "IOSScene_PushNotificationSettingsExample", + destinations: .iOS, product: .app, + deploymentTargets: .iOS(MINIMUM_IOS_VERSION), infoPlist: .file(path: "Resources/InfoPlist/InfoExample.plist"), dependencies: [ .target(name: "IOSScene_PushNotificationSettings"), .target(name: "DomainTesting"), ], appendSchemeTo: &schemes - ) - + Target.module( + ), + Target.makeIOSFramework( name: "IOSScene_GeneralSettings", + dependencies: [.target(name: "IOSSupport"),], resourceOptions: [.own], - dependencies: [ - .target(name: "IOSSupport"), - ], hasTests: true, additionalTestDependencies: [ .target(name: "DomainTesting"), .external(name: ExternalDependencyName.rxBlocking), ], appendSchemeTo: &schemes - ) - + Target.module( + ), + Target.makeTargets( name: "IPhoneDriver", - resourceOptions: [.own], + destinations: [.iPhone], + product: .framework, + deploymentTargets: .iOS(MINIMUM_IOS_VERSION), dependencies: [ .target(name: "IOSSupport"), .target(name: "IOSScene_WordChecking"), @@ -276,84 +299,56 @@ func targets() -> [Target] { .target(name: "IOSScene_ThemeSetting"), .external(name: ExternalDependencyName.swinjectDIContainer), ], + resourceOptions: [.own], appendSchemeTo: &disposedSchemes - ) - + Target.module( + ), + Target.makeTargets( name: PROJECT_NAME, + destinations: [.iPhone], product: .app, - bundleId: BASIC_BUNDLE_ID, + bundleID: BASIC_BUNDLE_ID, + deploymentTargets: .iOS(MINIMUM_IOS_VERSION), infoPlist: .file(path: "Resources/InfoPlist/Info.plist"), - resourceOptions: [ - .own, - .common, - ], - dependencies: [ - .target(name: "IPhoneDriver"), - ], + dependencies: [.target(name: "IPhoneDriver"),], settings: .settings(), + resourceOptions: [.own, .common], appendSchemeTo: &schemes - ) - + Target.module( + ), + Target.makeTargets( name: "\(PROJECT_NAME)Dev", + destinations: [.iPhone], product: .app, - bundleId: "\(BASIC_BUNDLE_ID)Dev", + bundleID: "\(BASIC_BUNDLE_ID)Dev", + deploymentTargets: .iOS(MINIMUM_IOS_VERSION), infoPlist: .file(path: "Resources/InfoPlist/Info.plist"), - resourceOptions: [ - .own, - .common, - ], - dependencies: [ - .target(name: "IPhoneDriver"), - ], - settings: .settings(), - appendSchemeTo: &schemes - ) - + Target.module( - name: "\(PROJECT_NAME)Mac", - platform: .macOS, - product: .app, - bundleId: "\(BASIC_BUNDLE_ID)Mac", - deploymentTarget: .macOS(targetVersion: "14.0.0"), - resourceOptions: [.own], - entitlements: nil, - dependencies: [], + dependencies: [.target(name: "IPhoneDriver"),], settings: .settings(), + resourceOptions: [.own, .common], appendSchemeTo: &schemes - ) - + Target.module( - name: "TestsSupport", - dependencies: [ - .target(name: "Domain"), - .external(name: ExternalDependencyName.rxSwift), - .external(name: ExternalDependencyName.rxTest), - ], - settings: .settings(base: ["ENABLE_TESTING_SEARCH_PATHS": "YES"]), - appendSchemeTo: &disposedSchemes - ) - + [ - Target.init( - name: "\(PROJECT_NAME)UITests", - platform: .iOS, - product: .uiTests, - bundleId: "\(BASIC_BUNDLE_ID)UITests", - deploymentTarget: DEPLOYMENT_TARGET, - sources: "Tests/\(PROJECT_NAME)UITests/**", - dependencies: [ - .target(name: "\(PROJECT_NAME)Dev"), - .target(name: "IOSSupport"), - .target(name: "Utility"), - .package(product: "Realm"), - .package(product: ExternalDependencyName.googleAPIClientForRESTCore), - .package(product: ExternalDependencyName.googleAPIClientForREST_Drive), - .package(product: ExternalDependencyName.googleSignIn), - ] - ), - ], + ), ] - - return targets.flatMap { $0 } + .flatMap { $0 } } +let macAppTargets = Target.makeTargets( + name: "\(PROJECT_NAME)Mac", + destinations: [.mac], + product: .app, + bundleID: "\(BASIC_BUNDLE_ID)Mac", + deploymentTargets: .macOS(MINIMUM_MACOS_VERSION), + dependencies: [], + settings: .settings(), + resourceOptions: [.own], + appendSchemeTo: &schemes +) + +let allTargets: [Target] = [ + commonTargets(), + iOSTargets(), + macAppTargets +] + .flatMap { $0 } + // MARK: - Project let project: Project = .init( @@ -369,37 +364,37 @@ let project: Project = .init( settings: .settings( base: ["SWIFT_EMIT_LOC_STRINGS": true] ), - targets: targets(), + targets: allTargets, schemes: schemes + [ - .init( + .scheme( name: PROJECT_NAME, buildAction: .buildAction(targets: ["\(PROJECT_NAME)"]), runAction: .runAction(executable: "\(PROJECT_NAME)"), profileAction: .profileAction(executable: "\(PROJECT_NAME)") ), - .init( + .scheme( name: "\(PROJECT_NAME)Dev", runAction: .runAction(executable: "\(PROJECT_NAME)Dev") ), - .init( + .scheme( name: "\(PROJECT_NAME)Dev_InMemoryDB", runAction: .runAction( executable: "\(PROJECT_NAME)Dev", - arguments: .init(launchArguments: [ - .init(name: "-useInMemoryDatabase", isEnabled: true) + arguments: .arguments(launchArguments: [ + .launchArgument(name: "-useInMemoryDatabase", isEnabled: true) ]) ) ), - .init( + .scheme( name: "\(PROJECT_NAME)Dev_SampleDB", runAction: .runAction( executable: "\(PROJECT_NAME)Dev", - arguments: .init(launchArguments: [ - .init(name: "-sampledDatabase", isEnabled: true) + arguments: .arguments(launchArguments: [ + .launchArgument(name: "-sampledDatabase", isEnabled: true) ]) ) ), - .init( + .scheme( name: "IntergrationTests", testAction: .testPlans([.relativeToRoot("TestPlans/IntergrationTests.xctestplan")]) ), diff --git a/Tuist/Dependencies.swift b/Tuist/Dependencies.swift deleted file mode 100644 index c8b7826b..00000000 --- a/Tuist/Dependencies.swift +++ /dev/null @@ -1,61 +0,0 @@ -import ProjectDescription - -let dependencies = Dependencies( - carthage: [ - ], - swiftPackageManager: SwiftPackageManagerDependencies( - [ - // RxSwift - .remote(url: "https://github.com/ReactiveX/RxSwift.git", - requirement: .upToNextMajor(from: "6.6.0")), - // Swinject - .remote(url: "https://github.com/Swinject/Swinject.git", - requirement: .upToNextMajor(from: "2.8.0")), - // SwinjectExtension - .remote(url: "https://github.com/Woin2ee-Modules/SwinjectExtension.git", - requirement: .upToNextMajor(from: "2.0.0")), - // SnapKit - .remote(url: "https://github.com/SnapKit/SnapKit.git", - requirement: .upToNextMajor(from: "5.0.1")), - // Realm - .remote(url: "https://github.com/realm/realm-swift.git", - requirement: .upToNextMajor(from: "10.42.0")), - // RxSwiftSugar - .remote(url: "https://github.com/Woin2ee-Modules/RxSwiftSugar.git", - requirement: .upToNextMajor(from: "1.0.0")), - // ExtendedUserDefaults - .remote(url: "https://github.com/Woin2ee-Modules/ExtendedUserDefaults.git", - requirement: .upToNextMajor(from: "1.0.0")), - // SFSafeSymbols - .remote(url: "https://github.com/SFSafeSymbols/SFSafeSymbols.git", - requirement: .upToNextMajor(from: "4.0.0")), - // Then - .remote(url: "https://github.com/devxoul/Then", - requirement: .upToNextMajor(from: "3.0.0")), - // Toast-Swift - .remote(url: "https://github.com/scalessec/Toast-Swift.git", - requirement: .upToNextMajor(from: "5.0.0")), - // GoogleSignIn-iOS - .remote(url: "https://github.com/google/GoogleSignIn-iOS", - requirement: .upToNextMajor(from: "6.0.2")), - // google-api-objectivec-client-for-rest - .remote(url: "https://github.com/google/google-api-objectivec-client-for-rest.git", - requirement: .upToNextMajor(from: "3.0.0")), - // ReactorKit - .remote(url: "https://github.com/ReactorKit/ReactorKit.git", - requirement: .upToNextMajor(from: "3.0.0")), - // FoundationPlus - .remote(url: "https://github.com/Woin2ee-Modules/FoundationPlus.git", - requirement: .upToNextMajor(from: "1.0.0")), - // UIKitPlus - .remote(url: "https://github.com/Woin2ee-Modules/UIKitPlus.git", - requirement: .upToNextMajor(from: "1.0.0")), - ], - baseSettings: .settings( - base: [ - "IPHONEOS_DEPLOYMENT_TARGET": "12.0" - ] - ) - ), - platforms: [.iOS] -) diff --git a/Tuist/Package.resolved b/Tuist/Package.resolved new file mode 100644 index 00000000..a8cc9269 --- /dev/null +++ b/Tuist/Package.resolved @@ -0,0 +1,185 @@ +{ + "pins" : [ + { + "identity" : "appauth-ios", + "kind" : "remoteSourceControl", + "location" : "https://github.com/openid/AppAuth-iOS.git", + "state" : { + "revision" : "71cde449f13d453227e687458144bde372d30fc7", + "version" : "1.6.2" + } + }, + { + "identity" : "extendeduserdefaults", + "kind" : "remoteSourceControl", + "location" : "https://github.com/Woin2ee-Modules/ExtendedUserDefaults.git", + "state" : { + "revision" : "921383d1c59c47f30a928f339dfe4244d8ba021e", + "version" : "1.1.0" + } + }, + { + "identity" : "foundationplus", + "kind" : "remoteSourceControl", + "location" : "https://github.com/Woin2ee-Modules/FoundationPlus.git", + "state" : { + "revision" : "bf488d2bbad79421ba2cbb19c8d104e6d4713506", + "version" : "1.3.0" + } + }, + { + "identity" : "google-api-objectivec-client-for-rest", + "kind" : "remoteSourceControl", + "location" : "https://github.com/google/google-api-objectivec-client-for-rest.git", + "state" : { + "revision" : "8b200e6e447c04140ac027cba4f368402766965f", + "version" : "3.5.1" + } + }, + { + "identity" : "googlesignin-ios", + "kind" : "remoteSourceControl", + "location" : "https://github.com/google/GoogleSignIn-iOS", + "state" : { + "revision" : "9c9b36af86a4dd3da16048a36cf37351e63ccfe1", + "version" : "6.2.4" + } + }, + { + "identity" : "gtm-session-fetcher", + "kind" : "remoteSourceControl", + "location" : "https://github.com/google/gtm-session-fetcher.git", + "state" : { + "revision" : "5ccda3981422a84186387dbb763ba739178b529c", + "version" : "2.3.0" + } + }, + { + "identity" : "gtmappauth", + "kind" : "remoteSourceControl", + "location" : "https://github.com/google/GTMAppAuth.git", + "state" : { + "revision" : "6dee0cde8a1b223737a5159e55e6b4ec16bbbdd9", + "version" : "1.3.1" + } + }, + { + "identity" : "reactorkit", + "kind" : "remoteSourceControl", + "location" : "https://github.com/ReactorKit/ReactorKit.git", + "state" : { + "revision" : "8fa33f09c6f6621a2aa536d739956d53b84dd139", + "version" : "3.2.0" + } + }, + { + "identity" : "realm-core", + "kind" : "remoteSourceControl", + "location" : "https://github.com/realm/realm-core.git", + "state" : { + "revision" : "a5e87a39cffdcc591f3203c11cfca68100d0b9a6", + "version" : "13.26.0" + } + }, + { + "identity" : "realm-swift", + "kind" : "remoteSourceControl", + "location" : "https://github.com/realm/realm-swift.git", + "state" : { + "revision" : "55466ae0fb29c5f21866792f05c1fc0c590d7c29", + "version" : "10.47.0" + } + }, + { + "identity" : "rxswift", + "kind" : "remoteSourceControl", + "location" : "https://github.com/ReactiveX/RxSwift.git", + "state" : { + "revision" : "9dcaa4b333db437b0fbfaf453fad29069044a8b4", + "version" : "6.6.0" + } + }, + { + "identity" : "rxswiftsugar", + "kind" : "remoteSourceControl", + "location" : "https://github.com/Woin2ee-Modules/RxSwiftSugar.git", + "state" : { + "revision" : "a55e3c0966d36b6ce116291b1045548a3d2a0cf4", + "version" : "1.2.1" + } + }, + { + "identity" : "sfsafesymbols", + "kind" : "remoteSourceControl", + "location" : "https://github.com/SFSafeSymbols/SFSafeSymbols.git", + "state" : { + "revision" : "7cca2d60925876b5953a2cf7341cd80fbeac983c", + "version" : "4.1.1" + } + }, + { + "identity" : "snapkit", + "kind" : "remoteSourceControl", + "location" : "https://github.com/SnapKit/SnapKit.git", + "state" : { + "revision" : "2842e6e84e82eb9a8dac0100ca90d9444b0307f4", + "version" : "5.7.1" + } + }, + { + "identity" : "swinject", + "kind" : "remoteSourceControl", + "location" : "https://github.com/Swinject/Swinject.git", + "state" : { + "revision" : "463cb2d659c8ae34899d18057ea5b1bb86fc3178", + "version" : "2.8.4" + } + }, + { + "identity" : "swinjectextension", + "kind" : "remoteSourceControl", + "location" : "https://github.com/Woin2ee-Modules/SwinjectExtension.git", + "state" : { + "revision" : "76948fa6de0ee7ea185844139e4b6aa44ab1ec65", + "version" : "2.0.0" + } + }, + { + "identity" : "then", + "kind" : "remoteSourceControl", + "location" : "https://github.com/devxoul/Then", + "state" : { + "revision" : "d41ef523faef0f911369f79c0b96815d9dbb6d7a", + "version" : "3.0.0" + } + }, + { + "identity" : "toast-swift", + "kind" : "remoteSourceControl", + "location" : "https://github.com/scalessec/Toast-Swift.git", + "state" : { + "revision" : "404ac98e0080a3d9f0630e007838284e04528bcd", + "version" : "5.1.0" + } + }, + { + "identity" : "uikitplus", + "kind" : "remoteSourceControl", + "location" : "https://github.com/Woin2ee-Modules/UIKitPlus.git", + "state" : { + "revision" : "9dbc90e9adc5cdc47e81f34fa1684d50cc7f0c84", + "version" : "1.0.1" + } + }, + { + "identity" : "weakmaptable", + "kind" : "remoteSourceControl", + "location" : "https://github.com/ReactorKit/WeakMapTable.git", + "state" : { + "revision" : "cb05d64cef2bbf51e85c53adee937df46540a74e", + "version" : "1.2.1" + } + } + ], + "version" : 2 +} diff --git a/Tuist/Package.swift b/Tuist/Package.swift new file mode 100644 index 00000000..12915e15 --- /dev/null +++ b/Tuist/Package.swift @@ -0,0 +1,52 @@ +// swift-tools-version: 5.9 +import PackageDescription + + +#if TUIST + import ProjectDescription + import ProjectDescriptionHelpers + + + let packageSettings = PackageSettings( + productTypes: [ + : + ] + ) +#endif + + +let package = Package( + name: "PackageName", + dependencies: [ + // RxSwift + .package(url: "https://github.com/ReactiveX/RxSwift.git", from: "6.6.0"), + // Swinject + .package(url: "https://github.com/Swinject/Swinject.git", from: "2.8.0"), + // SwinjectExtension + .package(url: "https://github.com/Woin2ee-Modules/SwinjectExtension.git", from: "2.0.0"), + // SnapKit + .package(url: "https://github.com/SnapKit/SnapKit.git", from: "5.0.1"), + // Realm + .package(url: "https://github.com/realm/realm-swift.git", from: "10.42.0"), + // RxSwiftSugar + .package(url: "https://github.com/Woin2ee-Modules/RxSwiftSugar.git", from: "1.0.0"), + // ExtendedUserDefaults + .package(url: "https://github.com/Woin2ee-Modules/ExtendedUserDefaults.git", from: "1.0.0"), + // SFSafeSymbols + .package(url: "https://github.com/SFSafeSymbols/SFSafeSymbols.git", from: "4.0.0"), + // Then + .package(url: "https://github.com/devxoul/Then", from: "3.0.0"), + // Toast-Swift + .package(url: "https://github.com/scalessec/Toast-Swift.git", from: "5.0.0"), + // GoogleSignIn-iOS + .package(url: "https://github.com/google/GoogleSignIn-iOS", from: "6.0.2"), + // google-api-objectivec-client-for-rest + .package(url: "https://github.com/google/google-api-objectivec-client-for-rest.git", from: "3.0.0"), + // ReactorKit + .package(url: "https://github.com/ReactorKit/ReactorKit.git", from: "3.0.0"), + // FoundationPlus + .package(url: "https://github.com/Woin2ee-Modules/FoundationPlus.git", from: "1.0.0"), + // UIKitPlus + .package(url: "https://github.com/Woin2ee-Modules/UIKitPlus.git", from: "1.0.0"), + ] +) diff --git a/Tuist/ProjectDescriptionHelpers/Configurations.swift b/Tuist/ProjectDescriptionHelpers/Configurations.swift index bd0c8b6c..a0bdd361 100644 --- a/Tuist/ProjectDescriptionHelpers/Configurations.swift +++ b/Tuist/ProjectDescriptionHelpers/Configurations.swift @@ -16,6 +16,13 @@ public let PROJECT_NAME = "WordChecker" public let BASIC_BUNDLE_ID = "com.\(ORGANIZATION).\(PROJECT_NAME)" -public let DEPLOYMENT_TARGET: DeploymentTarget = .iOS(targetVersion: "16.0", devices: [.iphone]) +public let MINIMUM_IOS_VERSION = "16.0" -public let XCODE_VERSION: Version? = .init(14, 3, 1) +public let MINIMUM_MACOS_VERSION = "14.0.0" + +public let ALL_DEPLOYMENT_TARGETS: DeploymentTargets = .multiplatform( + iOS: MINIMUM_IOS_VERSION, + macOS: MINIMUM_MACOS_VERSION +) + +public let ALL_DESTINATIONS: Destinations = [.iPhone, .mac] diff --git a/Tuist/ProjectDescriptionHelpers/Extensions/DeploymentTarget+platform.swift b/Tuist/ProjectDescriptionHelpers/Extensions/DeploymentTarget+platform.swift deleted file mode 100644 index 4349b554..00000000 --- a/Tuist/ProjectDescriptionHelpers/Extensions/DeploymentTarget+platform.swift +++ /dev/null @@ -1,29 +0,0 @@ -// -// DeploymentTarget+platform.swift -// ProjectDescriptionHelpers -// -// Created by Jaewon Yun on 2023/05/19. -// - -import ProjectDescription - -extension DeploymentTarget { - - /// 해당 `DeploymentTarget` 에 대한 `platform` 을 반환합니다. - public var platform: Platform { - switch self { - case .iOS: - return .iOS - case .macOS: - return .macOS - case .watchOS: - return .watchOS - case .tvOS: - return .tvOS - case .visionOS: - return .visionOS - @unknown default: - fatalError("Undefined platform. \(#file) \(#line)") - } - } -} diff --git a/Tuist/ProjectDescriptionHelpers/Extensions/Target+module.swift b/Tuist/ProjectDescriptionHelpers/Extensions/Target+module.swift index 74929295..cb1abbb2 100644 --- a/Tuist/ProjectDescriptionHelpers/Extensions/Target+module.swift +++ b/Tuist/ProjectDescriptionHelpers/Extensions/Target+module.swift @@ -19,23 +19,20 @@ public enum ResourceOption { } extension Target { - - public static func module( + + static func _module( name: String, - platform: ProjectDescription.Platform = .iOS, - product: ProjectDescription.Product = .framework, - bundleId: String? = nil, - deploymentTarget: ProjectDescription.DeploymentTarget = DEPLOYMENT_TARGET, + destinations: Destinations, + product: Product, + bundleID: String? = nil, + deploymentTargets: DeploymentTargets? = nil, // 동적으로 destinations 에 등록된 것만 적용되는가? -> 되면 default value 로 해결 가능 infoPlist: InfoPlist? = .default, + scripts: [TargetScript] = [], + dependencies: [TargetDependency] = [], + settings: Settings? = nil, + launchArguments: [LaunchArgument] = [], + additionalFiles: [FileElement] = [], resourceOptions: [ResourceOption] = [], - entitlements: ProjectDescription.Path? = nil, - scripts: [ProjectDescription.TargetScript] = [], - dependencies: [ProjectDescription.TargetDependency] = [], - settings: ProjectDescription.Settings? = nil, - coreDataModels: [ProjectDescription.CoreDataModel] = [], - environment: [String: String] = [:], - launchArguments: [ProjectDescription.LaunchArgument] = [], - additionalFiles: [ProjectDescription.FileElement] = [], hasTests: Bool = false, additionalTestDependencies: [ProjectDescription.TargetDependency] = [], appendSchemeTo schemes: inout [Scheme] @@ -54,12 +51,8 @@ extension Target { namePrefix = nameComponents[0] nameSuffix = nameComponents[1] } - - let bundleID = if bundleId != nil { - bundleId! - } else { - "\(BASIC_BUNDLE_ID).\(name.replacing("_", with: "."))" - } + + let bundleID = bundleID ?? "\(BASIC_BUNDLE_ID).\(name.replacing("_", with: "."))" let resourceFileElements = resourceOptions .reduce(into: [ResourceFileElement](), { partialResult, option in @@ -77,8 +70,8 @@ extension Target { partialResult.append(resourceFileElement) } }) - let resources: ResourceFileElements = .init(resources: resourceFileElements) - + let resources: ResourceFileElements = .resources(resourceFileElements) + let scheme: Scheme = { if hasTests { let testPlanName: String = if let namePrefix = namePrefix, let nameSuffix = nameSuffix { @@ -86,15 +79,17 @@ extension Target { } else { "TestPlans/\(name).xctestplan" } - - return Scheme( + + return Scheme.scheme( name: name, + shared: true, buildAction: .buildAction(targets: ["\(name)"]), testAction: .testPlans([.relativeToRoot(testPlanName)]) ) } else { - return Scheme( + return Scheme.scheme( name: name, + shared: true, buildAction: .buildAction(targets: ["\(name)"]) ) } @@ -107,27 +102,29 @@ extension Target { "Sources/\(name)/**" } - let frameworkTarget: Target = .init( + let frameworkTarget: Target = .target( name: name, - platform: platform, + destinations: destinations, product: product, productName: nil, bundleId: bundleID, - deploymentTarget: deploymentTarget, + deploymentTargets: deploymentTargets, infoPlist: infoPlist, sources: sources, resources: resources, copyFiles: nil, headers: nil, - entitlements: entitlements, + entitlements: nil, scripts: scripts, dependencies: dependencies, settings: settings, - coreDataModels: coreDataModels, - environment: environment, + coreDataModels: [], + environmentVariables: [:], launchArguments: launchArguments, additionalFiles: additionalFiles, - buildRules: [] + buildRules: [], + mergedBinaryType: .disabled, + mergeable: false ) if hasTests { @@ -139,33 +136,133 @@ extension Target { } let testBundleID = "\(BASIC_BUNDLE_ID).\(testsTargetName.replacing("_", with: "."))" - let testsTarget: Target = .init( + let testsTarget: Target = .target( name: testsTargetName, - platform: platform, + destinations: destinations, product: .unitTests, productName: nil, bundleId: testBundleID, - deploymentTarget: deploymentTarget, + deploymentTargets: deploymentTargets, infoPlist: .default, sources: testsSources, resources: resources, copyFiles: nil, headers: nil, - entitlements: entitlements, - scripts: scripts, + entitlements: nil, + scripts: [], dependencies: [.target(name: name)] + additionalTestDependencies, - settings: settings, - coreDataModels: coreDataModels, - environment: environment, - launchArguments: launchArguments, - additionalFiles: additionalFiles, - buildRules: [] + settings: nil, + coreDataModels: [], + environmentVariables: [:], + launchArguments: [], + additionalFiles: [], + buildRules: [], + mergedBinaryType: .disabled, + mergeable: false ) - + return [frameworkTarget, testsTarget] } else { return [frameworkTarget] } } + + public static func makeTargets( + name: String, + destinations: Destinations, + product: Product, + bundleID: String? = nil, + deploymentTargets: DeploymentTargets? = nil, + infoPlist: InfoPlist? = .default, + scripts: [TargetScript] = [], + dependencies: [TargetDependency] = [], + settings: Settings? = nil, + launchArguments: [LaunchArgument] = [], + additionalFiles: [FileElement] = [], + resourceOptions: [ResourceOption] = [], + hasTests: Bool = false, + additionalTestDependencies: [ProjectDescription.TargetDependency] = [], + appendSchemeTo schemes: inout [Scheme] + ) -> [Target] { + return Target._module( + name: name, + destinations: destinations, + product: product, + bundleID: bundleID, + deploymentTargets: deploymentTargets, + infoPlist: infoPlist, + scripts: scripts, + dependencies: dependencies, + settings: settings, + launchArguments: launchArguments, + additionalFiles: additionalFiles, + resourceOptions: resourceOptions, + hasTests: hasTests, + additionalTestDependencies: additionalTestDependencies, + appendSchemeTo: &schemes + ) + } + + public static func makeCommonFramework( + name: String, + scripts: [TargetScript] = [], + dependencies: [TargetDependency] = [], + settings: Settings? = nil, + launchArguments: [LaunchArgument] = [], + additionalFiles: [FileElement] = [], + resourceOptions: [ResourceOption] = [], + hasTests: Bool = false, + additionalTestDependencies: [ProjectDescription.TargetDependency] = [], + appendSchemeTo schemes: inout [Scheme] + ) -> [Target] { + return Target._module( + name: name, + destinations: ALL_DESTINATIONS, + product: .framework, + bundleID: nil, + deploymentTargets: ALL_DEPLOYMENT_TARGETS, + infoPlist: .default, + scripts: scripts, + dependencies: dependencies, + settings: settings, + launchArguments: launchArguments, + additionalFiles: additionalFiles, + resourceOptions: resourceOptions, + hasTests: hasTests, + additionalTestDependencies: additionalTestDependencies, + appendSchemeTo: &schemes + ) + } + + public static func makeIOSFramework( + name: String, + scripts: [TargetScript] = [], + dependencies: [TargetDependency] = [], + settings: Settings? = nil, + launchArguments: [LaunchArgument] = [], + additionalFiles: [FileElement] = [], + resourceOptions: [ResourceOption] = [], + hasTests: Bool = false, + additionalTestDependencies: [ProjectDescription.TargetDependency] = [], + appendSchemeTo schemes: inout [Scheme] + ) -> [Target] { + return Target._module( + name: name, + destinations: .iOS, + product: .framework, + bundleID: nil, + deploymentTargets: .iOS(MINIMUM_IOS_VERSION), + infoPlist: .default, + scripts: scripts, + dependencies: dependencies, + settings: settings, + launchArguments: launchArguments, + additionalFiles: additionalFiles, + resourceOptions: resourceOptions, + hasTests: hasTests, + additionalTestDependencies: additionalTestDependencies, + appendSchemeTo: &schemes + ) + } } diff --git a/Tuist/ProjectDescriptionHelpers/Scheme+Templates.swift b/Tuist/ProjectDescriptionHelpers/Scheme+Templates.swift deleted file mode 100644 index 2383d6f9..00000000 --- a/Tuist/ProjectDescriptionHelpers/Scheme+Templates.swift +++ /dev/null @@ -1,16 +0,0 @@ -// -// Scheme+Templates.swift -// ProjectDescriptionHelpers -// -// Created by Jaewon Yun on 2023/05/22. -// - -import ProjectDescription - -extension Scheme { - - public static func hideScheme(name: String) -> Scheme { - return .init(name: name, hidden: true) - } - -} From 3807e47516a63bd5ecb9ae8d9f541f0d64cca778 Mon Sep 17 00:00:00 2001 From: Jaewon Yun Date: Fri, 1 Mar 2024 00:42:48 +0900 Subject: [PATCH 10/11] Fix bug related to adding word --- .../WordAddition/WordAdditionViewController.swift | 9 +++++---- .../WordAddition/WordAdditionViewModel.swift | 14 +++++++++----- 2 files changed, 14 insertions(+), 9 deletions(-) diff --git a/Sources/IOSScene/WordAddition/WordAdditionViewController.swift b/Sources/IOSScene/WordAddition/WordAdditionViewController.swift index 7a400fe7..c18f4379 100644 --- a/Sources/IOSScene/WordAddition/WordAdditionViewController.swift +++ b/Sources/IOSScene/WordAddition/WordAdditionViewController.swift @@ -42,6 +42,7 @@ final class WordAdditionViewController: RxBaseViewController, WordAdditionViewCo lazy var doneBarButton: UIBarButtonItem = .init(systemItem: .done).then { $0.style = .done + $0.isEnabled = false } override func viewDidLoad() { @@ -116,10 +117,10 @@ final class WordAdditionViewController: RxBaseViewController, WordAdditionViewCo .distinctUntilChanged() .map { !$0 } .drive(duplicatedWordAlertLabel.rx.isHidden), - Driver.zip([ - output.wordTextIsNotEmpty, - output.enteredWordIsDuplicated, - ]).map { $0[0] && !$0[1] } + Driver.combineLatest( + output.wordIsEntered, + output.enteredWordIsDuplicated + ).map { $0.0 && !$0.1 } .drive(doneBarButton.rx.isEnabled), ] .forEach { $0.disposed(by: disposeBag) } diff --git a/Sources/IOSScene/WordAddition/WordAdditionViewModel.swift b/Sources/IOSScene/WordAddition/WordAdditionViewModel.swift index 3531145e..09024581 100644 --- a/Sources/IOSScene/WordAddition/WordAdditionViewModel.swift +++ b/Sources/IOSScene/WordAddition/WordAdditionViewModel.swift @@ -40,7 +40,7 @@ final class WordAdditionViewModel: ViewModelType { GlobalAction.shared.didAddWord.accept(()) } - let wordTextHasElements = input.wordText.map(\.hasElements) + let wordIsEntered = input.wordText.map(\.hasElements) let reconfirmDismiss = input.dismissAttempt .withLatestFrom(hasChanges) @@ -56,13 +56,17 @@ final class WordAdditionViewModel: ViewModelType { let enteredWordIsDuplicated = input.wordText .flatMapLatest { word in - return self.wordUseCase.isWordDuplicated(word) - .asDriverOnErrorJustComplete() + if word.hasElements { + return self.wordUseCase.isWordDuplicated(word) + .asDriverOnErrorJustComplete() + } else { + return .just(false) + } } return .init( saveComplete: saveComplete, - wordTextIsNotEmpty: wordTextHasElements, + wordIsEntered: wordIsEntered, reconfirmDismiss: reconfirmDismiss, dismissComplete: dismissComplete, enteredWordIsDuplicated: enteredWordIsDuplicated @@ -87,7 +91,7 @@ extension WordAdditionViewModel { let saveComplete: Signal - let wordTextIsNotEmpty: Driver + let wordIsEntered: Driver /// Dismiss 해야하는지 재확인이 필요할때 next 이벤트가 방출됩니다. let reconfirmDismiss: Signal From 66cb0daf40fc7da485c86a2e60a8380aa2ee27e7 Mon Sep 17 00:00:00 2001 From: Jaewon Yun Date: Fri, 1 Mar 2024 00:44:43 +0900 Subject: [PATCH 11/11] Add changelog --- Changelog/1.7.3.md | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 Changelog/1.7.3.md diff --git a/Changelog/1.7.3.md b/Changelog/1.7.3.md new file mode 100644 index 00000000..35b467bf --- /dev/null +++ b/Changelog/1.7.3.md @@ -0,0 +1,2 @@ +## Fixed +- Fixed bug related to adding word.