diff --git a/SOOUM/Podfile b/SOOUM/Podfile index 15dc37e4..91759f34 100644 --- a/SOOUM/Podfile +++ b/SOOUM/Podfile @@ -23,8 +23,11 @@ def pods pod 'Then', '~> 3.0.0' pod 'Kingfisher', '~> 7.10.0' pod 'YPImagePicker', '~> 5.2.2' + pod 'SwiftEntryKit', '~> 2.0.0' pod 'CocoaLumberjack/Swift', '~> 3.7.2' + + pod 'lottie-ios' end target 'SOOUM-Dev' do diff --git a/SOOUM/Podfile.lock b/SOOUM/Podfile.lock index 4f8b5a2c..29b8109f 100644 --- a/SOOUM/Podfile.lock +++ b/SOOUM/Podfile.lock @@ -127,6 +127,7 @@ PODS: - GoogleUtilities/Logger - GoogleUtilities/Privacy - Kingfisher (7.10.2) + - lottie-ios (4.5.1) - nanopb (2.30910.0): - nanopb/decode (= 2.30910.0) - nanopb/encode (= 2.30910.0) @@ -153,6 +154,7 @@ PODS: - RxSwift (6.8.0) - SnapKit (5.7.1) - SteviaLayout (5.1.2) + - SwiftEntryKit (2.0.0) - SwiftLint (0.56.2) - Then (3.0.0) - WeakMapTable (1.2.0) @@ -168,12 +170,14 @@ DEPENDENCIES: - Firebase/Crashlytics (~> 10.22.0) - Firebase/Messaging (~> 10.22.0) - Kingfisher (~> 7.10.0) + - lottie-ios - ReactorKit (~> 3.2.0) - RxCocoa (~> 6.8.0) - RxGesture (~> 4.0.4) - RxKeyboard (~> 2.0.0) - RxSwift (~> 6.8.0) - SnapKit (~> 5.7.1) + - SwiftEntryKit (~> 2.0.0) - SwiftLint (~> 0.56.2) - Then (~> 3.0.0) - YPImagePicker (~> 5.2.2) @@ -196,6 +200,7 @@ SPEC REPOS: - GoogleDataTransport - GoogleUtilities - Kingfisher + - lottie-ios - nanopb - PromisesObjC - PromisesSwift @@ -208,6 +213,7 @@ SPEC REPOS: - RxSwift - SnapKit - SteviaLayout + - SwiftEntryKit - SwiftLint - Then - WeakMapTable @@ -230,6 +236,7 @@ SPEC CHECKSUMS: GoogleDataTransport: 6c09b596d841063d76d4288cc2d2f42cc36e1e2a GoogleUtilities: ea963c370a38a8069cc5f7ba4ca849a60b6d7d15 Kingfisher: 99edc495d3b7607e6425f0d6f6847b2abd6d716d + lottie-ios: 248b380fa1b97d18e792c37d90da7ab2aa0d6562 nanopb: 438bc412db1928dac798aa6fd75726007be04262 PromisesObjC: f5707f49cb48b9636751c5b2e7d227e43fba9f47 PromisesSwift: 9d77319bbe72ebf6d872900551f7eeba9bce2851 @@ -242,11 +249,12 @@ SPEC CHECKSUMS: RxSwift: 4e28be97cbcfeee614af26d83415febbf2bf6f45 SnapKit: d612e99e678a2d3b95bf60b0705ed0a35c03484a SteviaLayout: 05424e528d643657d39ce72c786e4adf13cfc5f2 + SwiftEntryKit: 61b5fa36f34a97dd8013e48a7345bc4c4720be9a SwiftLint: bd7cfb914762ab5f0cbb632964849571db075706 Then: 844265ae87834bbe1147d91d5d41a404da2ec27d WeakMapTable: 05c694ce8439a7a9ebabb56187287a63c57673d6 YPImagePicker: afc81b3cffab05a6e7261c5daf80dc31b4e917b4 -PODFILE CHECKSUM: 77a47a04877511a9af2378cb76d7e51ca6d46f27 +PODFILE CHECKSUM: f064785e258ff9ada0428a59015c8a3fd3ccfe5d COCOAPODS: 1.16.2 diff --git a/SOOUM/SOOUM.xcodeproj/project.pbxproj b/SOOUM/SOOUM.xcodeproj/project.pbxproj index a78bfb81..c84d86e9 100644 --- a/SOOUM/SOOUM.xcodeproj/project.pbxproj +++ b/SOOUM/SOOUM.xcodeproj/project.pbxproj @@ -19,11 +19,9 @@ 2A44A42A2CAC09AE00DC463E /* RSAKeyResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A44A4292CAC09AE00DC463E /* RSAKeyResponse.swift */; }; 2A44A42B2CAC09AE00DC463E /* RSAKeyResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A44A4292CAC09AE00DC463E /* RSAKeyResponse.swift */; }; 2A44A42D2CAC14C800DC463E /* SignInResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A44A42C2CAC14C800DC463E /* SignInResponse.swift */; }; - 2A44A4342CAC21A500DC463E /* SignUpResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A44A4332CAC21A500DC463E /* SignUpResponse.swift */; }; - 2A44A4352CAC21A500DC463E /* SignUpResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A44A4332CAC21A500DC463E /* SignUpResponse.swift */; }; 2A44A4372CAC227300DC463E /* BaseAuthResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A44A4362CAC227300DC463E /* BaseAuthResponse.swift */; }; 2A44A4382CAC227300DC463E /* BaseAuthResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A44A4362CAC227300DC463E /* BaseAuthResponse.swift */; }; - 2A45B36D2CE3A3E30071026A /* ProfileImageSettingViewReactor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A5BB7F92CE277AF00E1C799 /* ProfileImageSettingViewReactor.swift */; }; + 2A45B36D2CE3A3E30071026A /* OnboardingProfileImageSettingViewReactor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A5BB7F92CE277AF00E1C799 /* OnboardingProfileImageSettingViewReactor.swift */; }; 2A45B36F2CE4C5510071026A /* RegisterUserResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A45B36E2CE4C5510071026A /* RegisterUserResponse.swift */; }; 2A45B3702CE4C5510071026A /* RegisterUserResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A45B36E2CE4C5510071026A /* RegisterUserResponse.swift */; }; 2A5ABA342D464E0B00BF6C9B /* ConfigureRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A5ABA332D464E0B00BF6C9B /* ConfigureRequest.swift */; }; @@ -34,8 +32,8 @@ 2A5BB7BF2CDB870000E1C799 /* OnboardingGuideMessageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A5BB7BD2CDB870000E1C799 /* OnboardingGuideMessageView.swift */; }; 2A5BB7C92CDBA53E00E1C799 /* OnboardingNicknameSettingViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A5BB7C82CDBA53E00E1C799 /* OnboardingNicknameSettingViewController.swift */; }; 2A5BB7CA2CDBA53E00E1C799 /* OnboardingNicknameSettingViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A5BB7C82CDBA53E00E1C799 /* OnboardingNicknameSettingViewController.swift */; }; - 2A5BB7CD2CDBB7D100E1C799 /* ProfileImageSettingViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A5BB7CC2CDBB7D100E1C799 /* ProfileImageSettingViewController.swift */; }; - 2A5BB7CE2CDBB7D100E1C799 /* ProfileImageSettingViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A5BB7CC2CDBB7D100E1C799 /* ProfileImageSettingViewController.swift */; }; + 2A5BB7CD2CDBB7D100E1C799 /* OnboardingProfileImageSettingViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A5BB7CC2CDBB7D100E1C799 /* OnboardingProfileImageSettingViewController.swift */; }; + 2A5BB7CE2CDBB7D100E1C799 /* OnboardingProfileImageSettingViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A5BB7CC2CDBB7D100E1C799 /* OnboardingProfileImageSettingViewController.swift */; }; 2A5BB7D12CDC7ADC00E1C799 /* OnboardingViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A5BB7D02CDC7ADC00E1C799 /* OnboardingViewController.swift */; }; 2A5BB7D22CDC7ADC00E1C799 /* OnboardingViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A5BB7D02CDC7ADC00E1C799 /* OnboardingViewController.swift */; }; 2A5BB7D52CDCA5C900E1C799 /* TermsOfServiceAgreeButtonView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A5BB7D42CDCA5C900E1C799 /* TermsOfServiceAgreeButtonView.swift */; }; @@ -48,7 +46,7 @@ 2A5BB7E42CDCD97300E1C799 /* JoinRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A5BB7E22CDCD97300E1C799 /* JoinRequest.swift */; }; 2A5BB7E72CDCDC3600E1C799 /* NicknameValidationResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A5BB7E62CDCDC3600E1C799 /* NicknameValidationResponse.swift */; }; 2A5BB7E82CDCDC3600E1C799 /* NicknameValidationResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A5BB7E62CDCDC3600E1C799 /* NicknameValidationResponse.swift */; }; - 2A5BB7FA2CE277AF00E1C799 /* ProfileImageSettingViewReactor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A5BB7F92CE277AF00E1C799 /* ProfileImageSettingViewReactor.swift */; }; + 2A5BB7FA2CE277AF00E1C799 /* OnboardingProfileImageSettingViewReactor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A5BB7F92CE277AF00E1C799 /* OnboardingProfileImageSettingViewReactor.swift */; }; 2A62805B2D084FEB00803BE9 /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 2A62805A2D084FEB00803BE9 /* GoogleService-Info.plist */; }; 2A649ECF2CAE8970002D8284 /* SOMDialogViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A649ECE2CAE8970002D8284 /* SOMDialogViewController.swift */; }; 2A649ED42CAE990B002D8284 /* SOMDialogViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A649ECE2CAE8970002D8284 /* SOMDialogViewController.swift */; }; @@ -60,8 +58,6 @@ 2A980BA52D803EEA007DFA45 /* SOMEvent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A980BA32D803EE2007DFA45 /* SOMEvent.swift */; }; 2A980BA82D803F04007DFA45 /* GAManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A980BA72D803F04007DFA45 /* GAManager.swift */; }; 2A980BA92D803F04007DFA45 /* GAManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A980BA72D803F04007DFA45 /* GAManager.swift */; }; - 2ACBD4132CC944FB0057C013 /* UploadRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2ACBD4122CC944FB0057C013 /* UploadRequest.swift */; }; - 2ACBD4142CC944FB0057C013 /* UploadRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2ACBD4122CC944FB0057C013 /* UploadRequest.swift */; }; 2ACBD4172CC963390057C013 /* DefaultCardImageResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2ACBD4162CC963390057C013 /* DefaultCardImageResponse.swift */; }; 2ACBD4182CC963390057C013 /* DefaultCardImageResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2ACBD4162CC963390057C013 /* DefaultCardImageResponse.swift */; }; 2ACBD41A2CCA03790057C013 /* ImageURLWithName.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2ACBD4192CCA03790057C013 /* ImageURLWithName.swift */; }; @@ -225,6 +221,18 @@ 38389B9D2CCCF98B006728AF /* AuthRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 38389B9B2CCCF98B006728AF /* AuthRequest.swift */; }; 38389B9F2CCCFB7D006728AF /* AuthKeyChain.swift in Sources */ = {isa = PBXBuildFile; fileRef = 38389B9E2CCCFB7D006728AF /* AuthKeyChain.swift */; }; 38389BA02CCCFB7D006728AF /* AuthKeyChain.swift in Sources */ = {isa = PBXBuildFile; fileRef = 38389B9E2CCCFB7D006728AF /* AuthKeyChain.swift */; }; + 383EC6112E7A4F6B00EC2D1E /* AuthLocalDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 383EC6102E7A4F5E00EC2D1E /* AuthLocalDataSource.swift */; }; + 383EC6122E7A4F6B00EC2D1E /* AuthLocalDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 383EC6102E7A4F5E00EC2D1E /* AuthLocalDataSource.swift */; }; + 383EC6152E7A50EB00EC2D1E /* AuthLocalDataSourceImpl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 383EC6142E7A50E000EC2D1E /* AuthLocalDataSourceImpl.swift */; }; + 383EC6162E7A50EB00EC2D1E /* AuthLocalDataSourceImpl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 383EC6142E7A50E000EC2D1E /* AuthLocalDataSourceImpl.swift */; }; + 383EC6192E7A547900EC2D1E /* BaseAssembler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 383EC6182E7A546B00EC2D1E /* BaseAssembler.swift */; }; + 383EC61A2E7A547900EC2D1E /* BaseAssembler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 383EC6182E7A546B00EC2D1E /* BaseAssembler.swift */; }; + 383EC61C2E7A548E00EC2D1E /* BaseDIContainer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 383EC61B2E7A548600EC2D1E /* BaseDIContainer.swift */; }; + 383EC61D2E7A548E00EC2D1E /* BaseDIContainer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 383EC61B2E7A548600EC2D1E /* BaseDIContainer.swift */; }; + 383EC6202E7A564600EC2D1E /* AppAssembler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 383EC61F2E7A564200EC2D1E /* AppAssembler.swift */; }; + 383EC6212E7A564600EC2D1E /* AppAssembler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 383EC61F2E7A564200EC2D1E /* AppAssembler.swift */; }; + 383EC6232E7A56CE00EC2D1E /* AppDIContainer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 383EC6222E7A56CA00EC2D1E /* AppDIContainer.swift */; }; + 383EC6242E7A56CE00EC2D1E /* AppDIContainer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 383EC6222E7A56CA00EC2D1E /* AppDIContainer.swift */; }; 38405CCB2CC611FD00612D1E /* BaseEmptyAndHeader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 38405CCA2CC611FD00612D1E /* BaseEmptyAndHeader.swift */; }; 38405CCC2CC611FD00612D1E /* BaseEmptyAndHeader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 38405CCA2CC611FD00612D1E /* BaseEmptyAndHeader.swift */; }; 3843C1BE2D4FB778009283AC /* MockAlamofire.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3843C1BD2D4FB778009283AC /* MockAlamofire.swift */; }; @@ -370,12 +378,106 @@ 388698632D1986B100008600 /* NotificationRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 388698612D1986B100008600 /* NotificationRequest.swift */; }; 388698652D1998DB00008600 /* NotificationTabBarReactor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 388698642D1998DB00008600 /* NotificationTabBarReactor.swift */; }; 388698662D1998DB00008600 /* NotificationTabBarReactor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 388698642D1998DB00008600 /* NotificationTabBarReactor.swift */; }; + 3887176A2E7BD7AC00C6143B /* loading_indicator_lottie.json in Resources */ = {isa = PBXBuildFile; fileRef = 388717692E7BD7AC00C6143B /* loading_indicator_lottie.json */; }; + 3887176B2E7BD7AC00C6143B /* loading_indicator_lottie.json in Resources */ = {isa = PBXBuildFile; fileRef = 388717692E7BD7AC00C6143B /* loading_indicator_lottie.json */; }; + 3887176D2E7BDBAE00C6143B /* NicknameResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3887176C2E7BDBA800C6143B /* NicknameResponse.swift */; }; + 3887176E2E7BDBAE00C6143B /* NicknameResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3887176C2E7BDBA800C6143B /* NicknameResponse.swift */; }; 3887D0332CC5335200FB52E1 /* WriteCardViewReactor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3887D0322CC5335200FB52E1 /* WriteCardViewReactor.swift */; }; 3887D0342CC5335200FB52E1 /* WriteCardViewReactor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3887D0322CC5335200FB52E1 /* WriteCardViewReactor.swift */; }; 3887D0362CC5335D00FB52E1 /* WriteCardView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3887D0352CC5335D00FB52E1 /* WriteCardView.swift */; }; 3887D0372CC5335D00FB52E1 /* WriteCardView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3887D0352CC5335D00FB52E1 /* WriteCardView.swift */; }; 3887D0392CC5504500FB52E1 /* UITextField+Typography.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3887D0382CC5504500FB52E1 /* UITextField+Typography.swift */; }; 3887D03A2CC5504500FB52E1 /* UITextField+Typography.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3887D0382CC5504500FB52E1 /* UITextField+Typography.swift */; }; + 38899E582E7936DD0030F7CA /* SooumStyle_V2.swift in Sources */ = {isa = PBXBuildFile; fileRef = 38899E572E7936D50030F7CA /* SooumStyle_V2.swift */; }; + 38899E592E7936DD0030F7CA /* SooumStyle_V2.swift in Sources */ = {isa = PBXBuildFile; fileRef = 38899E572E7936D50030F7CA /* SooumStyle_V2.swift */; }; + 38899E5E2E7937E50030F7CA /* NicknameValidateResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = 38899E5D2E7937DB0030F7CA /* NicknameValidateResponse.swift */; }; + 38899E5F2E7937E50030F7CA /* NicknameValidateResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = 38899E5D2E7937DB0030F7CA /* NicknameValidateResponse.swift */; }; + 38899E662E7939600030F7CA /* CheckAvailableResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = 38899E652E79395E0030F7CA /* CheckAvailableResponse.swift */; }; + 38899E672E7939600030F7CA /* CheckAvailableResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = 38899E652E79395E0030F7CA /* CheckAvailableResponse.swift */; }; + 38899E6B2E793AFD0030F7CA /* CheckAvailable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 38899E6A2E793AF70030F7CA /* CheckAvailable.swift */; }; + 38899E6C2E793AFD0030F7CA /* CheckAvailable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 38899E6A2E793AF70030F7CA /* CheckAvailable.swift */; }; + 38899E6E2E79400C0030F7CA /* ImageUrlInfoResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = 38899E6D2E79400B0030F7CA /* ImageUrlInfoResponse.swift */; }; + 38899E6F2E79400C0030F7CA /* ImageUrlInfoResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = 38899E6D2E79400B0030F7CA /* ImageUrlInfoResponse.swift */; }; + 38899E712E79402C0030F7CA /* ImageUrlInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 38899E702E7940280030F7CA /* ImageUrlInfo.swift */; }; + 38899E722E79402C0030F7CA /* ImageUrlInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 38899E702E7940280030F7CA /* ImageUrlInfo.swift */; }; + 38899E742E79430A0030F7CA /* SignUpRequestInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 38899E732E7943070030F7CA /* SignUpRequestInfo.swift */; }; + 38899E752E79430A0030F7CA /* SignUpRequestInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 38899E732E7943070030F7CA /* SignUpRequestInfo.swift */; }; + 38899E7D2E794B420030F7CA /* SignUpResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = 38899E7C2E794B3D0030F7CA /* SignUpResponse.swift */; }; + 38899E7E2E794B420030F7CA /* SignUpResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = 38899E7C2E794B3D0030F7CA /* SignUpResponse.swift */; }; + 38899E832E794C360030F7CA /* LoginResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = 38899E822E794C330030F7CA /* LoginResponse.swift */; }; + 38899E842E794C360030F7CA /* LoginResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = 38899E822E794C330030F7CA /* LoginResponse.swift */; }; + 38899E862E794CEE0030F7CA /* NetworkManager_FCM.swift in Sources */ = {isa = PBXBuildFile; fileRef = 38899E852E794CE90030F7CA /* NetworkManager_FCM.swift */; }; + 38899E872E794CEE0030F7CA /* NetworkManager_FCM.swift in Sources */ = {isa = PBXBuildFile; fileRef = 38899E852E794CE90030F7CA /* NetworkManager_FCM.swift */; }; + 38899E892E794D620030F7CA /* NetworkManager_Version.swift in Sources */ = {isa = PBXBuildFile; fileRef = 38899E882E794D5D0030F7CA /* NetworkManager_Version.swift */; }; + 38899E8A2E794D620030F7CA /* NetworkManager_Version.swift in Sources */ = {isa = PBXBuildFile; fileRef = 38899E882E794D5D0030F7CA /* NetworkManager_Version.swift */; }; + 38899E8C2E794E690030F7CA /* AppVersionStatusResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = 38899E8B2E794E680030F7CA /* AppVersionStatusResponse.swift */; }; + 38899E8D2E794E690030F7CA /* AppVersionStatusResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = 38899E8B2E794E680030F7CA /* AppVersionStatusResponse.swift */; }; + 38899E8F2E7951200030F7CA /* KeyInfoResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = 38899E8E2E79511F0030F7CA /* KeyInfoResponse.swift */; }; + 38899E902E7951200030F7CA /* KeyInfoResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = 38899E8E2E79511F0030F7CA /* KeyInfoResponse.swift */; }; + 38899E932E79518F0030F7CA /* CommonNotificationInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 38899E922E79518E0030F7CA /* CommonNotificationInfo.swift */; }; + 38899E942E79518F0030F7CA /* CommonNotificationInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 38899E922E79518E0030F7CA /* CommonNotificationInfo.swift */; }; + 38899E962E7953310030F7CA /* NotificationInfoResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = 38899E952E7953300030F7CA /* NotificationInfoResponse.swift */; }; + 38899E972E7953310030F7CA /* NotificationInfoResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = 38899E952E7953300030F7CA /* NotificationInfoResponse.swift */; }; + 38899E992E7954680030F7CA /* BlockedNotificationInfoResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = 38899E982E7954670030F7CA /* BlockedNotificationInfoResponse.swift */; }; + 38899E9A2E7954680030F7CA /* BlockedNotificationInfoResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = 38899E982E7954670030F7CA /* BlockedNotificationInfoResponse.swift */; }; + 38899E9C2E7954D90030F7CA /* DeleteNotificationInfoResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = 38899E9B2E7954D70030F7CA /* DeleteNotificationInfoResponse.swift */; }; + 38899E9D2E7954D90030F7CA /* DeleteNotificationInfoResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = 38899E9B2E7954D70030F7CA /* DeleteNotificationInfoResponse.swift */; }; + 38899EA32E799B260030F7CA /* AppVersionRemoteDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 38899EA22E799B190030F7CA /* AppVersionRemoteDataSource.swift */; }; + 38899EA42E799B260030F7CA /* AppVersionRemoteDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 38899EA22E799B190030F7CA /* AppVersionRemoteDataSource.swift */; }; + 38899EA62E799BD60030F7CA /* AppVersionRemoteDataSourceImpl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 38899EA52E799BD10030F7CA /* AppVersionRemoteDataSourceImpl.swift */; }; + 38899EA72E799BD60030F7CA /* AppVersionRemoteDataSourceImpl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 38899EA52E799BD10030F7CA /* AppVersionRemoteDataSourceImpl.swift */; }; + 38899EA92E799C630030F7CA /* VersionRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 38899EA82E799C5D0030F7CA /* VersionRequest.swift */; }; + 38899EAA2E799C630030F7CA /* VersionRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 38899EA82E799C5D0030F7CA /* VersionRequest.swift */; }; + 38899EAD2E79A09B0030F7CA /* UserRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 38899EAC2E79A0990030F7CA /* UserRequest.swift */; }; + 38899EAE2E79A09B0030F7CA /* UserRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 38899EAC2E79A0990030F7CA /* UserRequest.swift */; }; + 3889A2432E79AD7D0030F7CA /* AppVersionRepository.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3889A2422E79AD600030F7CA /* AppVersionRepository.swift */; }; + 3889A2442E79AD7D0030F7CA /* AppVersionRepository.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3889A2422E79AD600030F7CA /* AppVersionRepository.swift */; }; + 3889A2462E79ADCE0030F7CA /* AppVersionRepositoryImpl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3889A2452E79ADBD0030F7CA /* AppVersionRepositoryImpl.swift */; }; + 3889A2472E79ADCE0030F7CA /* AppVersionRepositoryImpl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3889A2452E79ADBD0030F7CA /* AppVersionRepositoryImpl.swift */; }; + 3889A24A2E79AE960030F7CA /* AppVersionUseCase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3889A2492E79AE900030F7CA /* AppVersionUseCase.swift */; }; + 3889A24B2E79AE960030F7CA /* AppVersionUseCase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3889A2492E79AE900030F7CA /* AppVersionUseCase.swift */; }; + 3889A24D2E79AEB30030F7CA /* AppVersionUseCaseImpl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3889A24C2E79AEAD0030F7CA /* AppVersionUseCaseImpl.swift */; }; + 3889A24E2E79AEB30030F7CA /* AppVersionUseCaseImpl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3889A24C2E79AEAD0030F7CA /* AppVersionUseCaseImpl.swift */; }; + 3889A2502E79B3260030F7CA /* UserRemoteDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3889A24F2E79B3210030F7CA /* UserRemoteDataSource.swift */; }; + 3889A2512E79B3260030F7CA /* UserRemoteDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3889A24F2E79B3210030F7CA /* UserRemoteDataSource.swift */; }; + 3889A2562E79BA160030F7CA /* UserRemoteDataSourceImpl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3889A2552E79BA0F0030F7CA /* UserRemoteDataSourceImpl.swift */; }; + 3889A2572E79BA160030F7CA /* UserRemoteDataSourceImpl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3889A2552E79BA0F0030F7CA /* UserRemoteDataSourceImpl.swift */; }; + 3889A25C2E79BB340030F7CA /* UserRepository.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3889A25B2E79BB2F0030F7CA /* UserRepository.swift */; }; + 3889A25D2E79BB340030F7CA /* UserRepository.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3889A25B2E79BB2F0030F7CA /* UserRepository.swift */; }; + 3889A2622E79BB5B0030F7CA /* UserRepositoryImpl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3889A2612E79BB540030F7CA /* UserRepositoryImpl.swift */; }; + 3889A2632E79BB5B0030F7CA /* UserRepositoryImpl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3889A2612E79BB540030F7CA /* UserRepositoryImpl.swift */; }; + 3889A2652E79BBC40030F7CA /* UserUseCase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3889A2642E79BBC00030F7CA /* UserUseCase.swift */; }; + 3889A2662E79BBC40030F7CA /* UserUseCase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3889A2642E79BBC00030F7CA /* UserUseCase.swift */; }; + 3889A2682E79BC880030F7CA /* UserUseCaseImpl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3889A2672E79BC840030F7CA /* UserUseCaseImpl.swift */; }; + 3889A2692E79BC880030F7CA /* UserUseCaseImpl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3889A2672E79BC840030F7CA /* UserUseCaseImpl.swift */; }; + 3889A26B2E79BD450030F7CA /* AuthRemoteDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3889A26A2E79BD410030F7CA /* AuthRemoteDataSource.swift */; }; + 3889A26C2E79BD450030F7CA /* AuthRemoteDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3889A26A2E79BD410030F7CA /* AuthRemoteDataSource.swift */; }; + 3889A26E2E79BE9F0030F7CA /* AuthRemoteDataSourceImpl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3889A26D2E79BE970030F7CA /* AuthRemoteDataSourceImpl.swift */; }; + 3889A26F2E79BE9F0030F7CA /* AuthRemoteDataSourceImpl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3889A26D2E79BE970030F7CA /* AuthRemoteDataSourceImpl.swift */; }; + 3889A2712E79C03B0030F7CA /* AuthRepository.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3889A2702E79C0370030F7CA /* AuthRepository.swift */; }; + 3889A2722E79C03B0030F7CA /* AuthRepository.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3889A2702E79C0370030F7CA /* AuthRepository.swift */; }; + 3889A2742E79C1D80030F7CA /* NotificationRemoteDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3889A2732E79C1D30030F7CA /* NotificationRemoteDataSource.swift */; }; + 3889A2752E79C1D80030F7CA /* NotificationRemoteDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3889A2732E79C1D30030F7CA /* NotificationRemoteDataSource.swift */; }; + 3889A2772E79C29F0030F7CA /* NotificationRemoteDataSoruceImpl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3889A2762E79C2980030F7CA /* NotificationRemoteDataSoruceImpl.swift */; }; + 3889A2782E79C29F0030F7CA /* NotificationRemoteDataSoruceImpl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3889A2762E79C2980030F7CA /* NotificationRemoteDataSoruceImpl.swift */; }; + 3889A27D2E79C56E0030F7CA /* ToeknResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3889A27C2E79C5670030F7CA /* ToeknResponse.swift */; }; + 3889A27E2E79C56E0030F7CA /* ToeknResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3889A27C2E79C5670030F7CA /* ToeknResponse.swift */; }; + 3889A2802E79D0250030F7CA /* Token.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3889A27F2E79D0230030F7CA /* Token.swift */; }; + 3889A2812E79D0250030F7CA /* Token.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3889A27F2E79D0230030F7CA /* Token.swift */; }; + 3889A2832E79D7D40030F7CA /* AuthRepositoryImpl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3889A2822E79D7CE0030F7CA /* AuthRepositoryImpl.swift */; }; + 3889A2842E79D7D40030F7CA /* AuthRepositoryImpl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3889A2822E79D7CE0030F7CA /* AuthRepositoryImpl.swift */; }; + 3889A2862E79D8090030F7CA /* AuthUseCase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3889A2852E79D8060030F7CA /* AuthUseCase.swift */; }; + 3889A2872E79D8090030F7CA /* AuthUseCase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3889A2852E79D8060030F7CA /* AuthUseCase.swift */; }; + 3889A2892E79D8220030F7CA /* AuthUseCaseImpl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3889A2882E79D81E0030F7CA /* AuthUseCaseImpl.swift */; }; + 3889A28A2E79D8220030F7CA /* AuthUseCaseImpl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3889A2882E79D81E0030F7CA /* AuthUseCaseImpl.swift */; }; + 3889A28C2E79D86B0030F7CA /* NotificationRepository.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3889A28B2E79D8650030F7CA /* NotificationRepository.swift */; }; + 3889A28D2E79D86B0030F7CA /* NotificationRepository.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3889A28B2E79D8650030F7CA /* NotificationRepository.swift */; }; + 3889A28F2E79D8860030F7CA /* NotificationRepositoryImpl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3889A28E2E79D8800030F7CA /* NotificationRepositoryImpl.swift */; }; + 3889A2902E79D8860030F7CA /* NotificationRepositoryImpl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3889A28E2E79D8800030F7CA /* NotificationRepositoryImpl.swift */; }; + 3889A2922E79D8F80030F7CA /* NotificationUseCase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3889A2912E79D8F40030F7CA /* NotificationUseCase.swift */; }; + 3889A2932E79D8F80030F7CA /* NotificationUseCase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3889A2912E79D8F40030F7CA /* NotificationUseCase.swift */; }; + 3889A2952E79D9250030F7CA /* NotificationUseCaseImpl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3889A2942E79D9200030F7CA /* NotificationUseCaseImpl.swift */; }; + 3889A2962E79D9250030F7CA /* NotificationUseCaseImpl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3889A2942E79D9200030F7CA /* NotificationUseCaseImpl.swift */; }; 388A2D2D2D00A45800E2F2F0 /* writtenCardResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = 388A2D2C2D00A45800E2F2F0 /* writtenCardResponse.swift */; }; 388A2D2E2D00A45800E2F2F0 /* writtenCardResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = 388A2D2C2D00A45800E2F2F0 /* writtenCardResponse.swift */; }; 388A2D302D00D6A100E2F2F0 /* FollowViewReactor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 388A2D2F2D00D6A100E2F2F0 /* FollowViewReactor.swift */; }; @@ -384,6 +486,8 @@ 388A2D342D00D7BF00E2F2F0 /* UpdateProfileViewReactor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 388A2D322D00D7BF00E2F2F0 /* UpdateProfileViewReactor.swift */; }; 388C96362CCE41700061C598 /* AuthInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 388C96352CCE41700061C598 /* AuthInfo.swift */; }; 388C96372CCE41700061C598 /* AuthInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 388C96352CCE41700061C598 /* AuthInfo.swift */; }; + 388D8ADF2E73E6190044BA79 /* SwiftEntryKit.swift in Sources */ = {isa = PBXBuildFile; fileRef = 388D8ADE2E73E6130044BA79 /* SwiftEntryKit.swift */; }; + 388D8AE02E73E6190044BA79 /* SwiftEntryKit.swift in Sources */ = {isa = PBXBuildFile; fileRef = 388D8ADE2E73E6130044BA79 /* SwiftEntryKit.swift */; }; 388DA0FB2C8F521000A9DD56 /* FontContainer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 388DA0FA2C8F521000A9DD56 /* FontContainer.swift */; }; 388DA0FC2C8F521300A9DD56 /* FontContainer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 388DA0FA2C8F521000A9DD56 /* FontContainer.swift */; }; 388DA0FE2C8F526C00A9DD56 /* UIFont.swift in Sources */ = {isa = PBXBuildFile; fileRef = 388DA0FD2C8F526C00A9DD56 /* UIFont.swift */; }; @@ -410,14 +514,24 @@ 38A5D1552C8CB12300B68363 /* UIImage+SOOUM.swift in Sources */ = {isa = PBXBuildFile; fileRef = 38A5D1532C8CB11E00B68363 /* UIImage+SOOUM.swift */; }; 38A627172CECC5A800C37A03 /* SOMTagsLayoutConfigure.swift in Sources */ = {isa = PBXBuildFile; fileRef = 38A627162CECC5A800C37A03 /* SOMTagsLayoutConfigure.swift */; }; 38A627182CECC5A800C37A03 /* SOMTagsLayoutConfigure.swift in Sources */ = {isa = PBXBuildFile; fileRef = 38A627162CECC5A800C37A03 /* SOMTagsLayoutConfigure.swift */; }; + 38A721952E73E7140071E1D8 /* View+SwiftEntryKit.swift in Sources */ = {isa = PBXBuildFile; fileRef = 38A721942E73E7010071E1D8 /* View+SwiftEntryKit.swift */; }; + 38A721962E73E7140071E1D8 /* View+SwiftEntryKit.swift in Sources */ = {isa = PBXBuildFile; fileRef = 38A721942E73E7010071E1D8 /* View+SwiftEntryKit.swift */; }; + 38A721992E73EA6F0071E1D8 /* SelectProfileBottomFloatView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 38A721982E73EA610071E1D8 /* SelectProfileBottomFloatView.swift */; }; + 38A7219A2E73EA6F0071E1D8 /* SelectProfileBottomFloatView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 38A721982E73EA610071E1D8 /* SelectProfileBottomFloatView.swift */; }; 38AA00022CAD1BCC002C5F1E /* LikeAndCommentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 38AA00012CAD1BCC002C5F1E /* LikeAndCommentView.swift */; }; 38AA00032CAD1BCC002C5F1E /* LikeAndCommentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 38AA00012CAD1BCC002C5F1E /* LikeAndCommentView.swift */; }; 38AA00062CAD96E3002C5F1E /* MoreBottomSheetViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 38AA00052CAD96E3002C5F1E /* MoreBottomSheetViewController.swift */; }; 38AA00072CAD96E3002C5F1E /* MoreBottomSheetViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 38AA00052CAD96E3002C5F1E /* MoreBottomSheetViewController.swift */; }; - 38AA66262D3AC3F500B3F6B2 /* DialogMessageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 38AA66252D3AC3F500B3F6B2 /* DialogMessageView.swift */; }; - 38AA66272D3AC3F500B3F6B2 /* DialogMessageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 38AA66252D3AC3F500B3F6B2 /* DialogMessageView.swift */; }; 38AE565C2D048B4800CAA431 /* SOMDialogViewController+Show.swift in Sources */ = {isa = PBXBuildFile; fileRef = 38AE565B2D048B4800CAA431 /* SOMDialogViewController+Show.swift */; }; 38AE565D2D048B4800CAA431 /* SOMDialogViewController+Show.swift in Sources */ = {isa = PBXBuildFile; fileRef = 38AE565B2D048B4800CAA431 /* SOMDialogViewController+Show.swift */; }; + 38AE77D42E74580000B6FD13 /* OnboardingCompletedViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 38AE77D32E7457F400B6FD13 /* OnboardingCompletedViewController.swift */; }; + 38AE77D52E74580000B6FD13 /* OnboardingCompletedViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 38AE77D32E7457F400B6FD13 /* OnboardingCompletedViewController.swift */; }; + 38AE77D72E7459F400B6FD13 /* OnboardingCompletedViewReactor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 38AE77D62E7459EA00B6FD13 /* OnboardingCompletedViewReactor.swift */; }; + 38AE77D82E7459F400B6FD13 /* OnboardingCompletedViewReactor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 38AE77D62E7459EA00B6FD13 /* OnboardingCompletedViewReactor.swift */; }; + 38AE77DB2E745FFF00B6FD13 /* EnterMemberTransferTextFieldView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 38AE77DA2E745FF700B6FD13 /* EnterMemberTransferTextFieldView.swift */; }; + 38AE77DC2E745FFF00B6FD13 /* EnterMemberTransferTextFieldView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 38AE77DA2E745FF700B6FD13 /* EnterMemberTransferTextFieldView.swift */; }; + 38AE77DE2E7465F500B6FD13 /* EnterMemberTransferTextFieldView+Rx.swift in Sources */ = {isa = PBXBuildFile; fileRef = 38AE77DD2E7465E600B6FD13 /* EnterMemberTransferTextFieldView+Rx.swift */; }; + 38AE77DF2E7465F500B6FD13 /* EnterMemberTransferTextFieldView+Rx.swift in Sources */ = {isa = PBXBuildFile; fileRef = 38AE77DD2E7465E600B6FD13 /* EnterMemberTransferTextFieldView+Rx.swift */; }; 38B543DF2D46171300DDF2C5 /* ManagerConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 38B543DE2D46171300DDF2C5 /* ManagerConfiguration.swift */; }; 38B543E02D46171300DDF2C5 /* ManagerConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 38B543DE2D46171300DDF2C5 /* ManagerConfiguration.swift */; }; 38B543E22D46179500DDF2C5 /* AuthManagerConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 38B543E12D46179500DDF2C5 /* AuthManagerConfiguration.swift */; }; @@ -430,6 +544,10 @@ 38B543EC2D461B1A00DDF2C5 /* LocationManagerConfigruation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 38B543EA2D461B1A00DDF2C5 /* LocationManagerConfigruation.swift */; }; 38B543EE2D46506300DDF2C5 /* ManagerType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 38B543ED2D46506300DDF2C5 /* ManagerType.swift */; }; 38B543EF2D46506300DDF2C5 /* ManagerType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 38B543ED2D46506300DDF2C5 /* ManagerType.swift */; }; + 38B65E792E72A29F00DF6919 /* OnboardingNumberingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 38B65E782E72A29100DF6919 /* OnboardingNumberingView.swift */; }; + 38B65E7A2E72A29F00DF6919 /* OnboardingNumberingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 38B65E782E72A29100DF6919 /* OnboardingNumberingView.swift */; }; + 38B65E7C2E72ADB900DF6919 /* TermsOfServiceAgreeButtonView+Rx.swift in Sources */ = {isa = PBXBuildFile; fileRef = 38B65E7B2E72ADB500DF6919 /* TermsOfServiceAgreeButtonView+Rx.swift */; }; + 38B65E7D2E72ADB900DF6919 /* TermsOfServiceAgreeButtonView+Rx.swift in Sources */ = {isa = PBXBuildFile; fileRef = 38B65E7B2E72ADB500DF6919 /* TermsOfServiceAgreeButtonView+Rx.swift */; }; 38B6AACD2CA410D800CE6DB6 /* MainTabBarController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 38B6AACC2CA410D800CE6DB6 /* MainTabBarController.swift */; }; 38B6AACE2CA410D800CE6DB6 /* MainTabBarController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 38B6AACC2CA410D800CE6DB6 /* MainTabBarController.swift */; }; 38B6AAD82CA424AE00CE6DB6 /* MoveTopButtonView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 38B6AAD72CA424AE00CE6DB6 /* MoveTopButtonView.swift */; }; @@ -478,6 +596,8 @@ 38D3CB192CC2362B001EC280 /* Hakgyoansim-Light.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 38D3CB152CC2362B001EC280 /* Hakgyoansim-Light.ttf */; }; 38D488CA2D0C557300F2D38D /* SOMButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 38D488C92D0C557300F2D38D /* SOMButton.swift */; }; 38D488CB2D0C557300F2D38D /* SOMButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 38D488C92D0C557300F2D38D /* SOMButton.swift */; }; + 38D522682E742F610044911B /* SOMLoadingIndicatorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 38D522672E742F550044911B /* SOMLoadingIndicatorView.swift */; }; + 38D522692E742F610044911B /* SOMLoadingIndicatorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 38D522672E742F550044911B /* SOMLoadingIndicatorView.swift */; }; 38D5637B2D16D72D006265AA /* SOMSwipeTabBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 38D5637A2D16D72D006265AA /* SOMSwipeTabBar.swift */; }; 38D5637C2D16D72D006265AA /* SOMSwipeTabBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 38D5637A2D16D72D006265AA /* SOMSwipeTabBar.swift */; }; 38D5637E2D17152F006265AA /* SOMSwipeTabBarDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 38D5637D2D17152F006265AA /* SOMSwipeTabBarDelegate.swift */; }; @@ -580,21 +700,20 @@ 2A34AFB42D144EEF007BD7E7 /* EmptyTagDetailTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmptyTagDetailTableViewCell.swift; sourceTree = ""; }; 2A44A4292CAC09AE00DC463E /* RSAKeyResponse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RSAKeyResponse.swift; sourceTree = ""; }; 2A44A42C2CAC14C800DC463E /* SignInResponse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SignInResponse.swift; sourceTree = ""; }; - 2A44A4332CAC21A500DC463E /* SignUpResponse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SignUpResponse.swift; sourceTree = ""; }; 2A44A4362CAC227300DC463E /* BaseAuthResponse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BaseAuthResponse.swift; sourceTree = ""; }; 2A45B36E2CE4C5510071026A /* RegisterUserResponse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RegisterUserResponse.swift; sourceTree = ""; }; 2A5ABA332D464E0B00BF6C9B /* ConfigureRequest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConfigureRequest.swift; sourceTree = ""; }; 2A5BB7B82CDB860D00E1C799 /* OnboardingTermsOfServiceViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnboardingTermsOfServiceViewController.swift; sourceTree = ""; }; 2A5BB7BD2CDB870000E1C799 /* OnboardingGuideMessageView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnboardingGuideMessageView.swift; sourceTree = ""; }; 2A5BB7C82CDBA53E00E1C799 /* OnboardingNicknameSettingViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnboardingNicknameSettingViewController.swift; sourceTree = ""; }; - 2A5BB7CC2CDBB7D100E1C799 /* ProfileImageSettingViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileImageSettingViewController.swift; sourceTree = ""; }; + 2A5BB7CC2CDBB7D100E1C799 /* OnboardingProfileImageSettingViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnboardingProfileImageSettingViewController.swift; sourceTree = ""; }; 2A5BB7D02CDC7ADC00E1C799 /* OnboardingViewController.swift */ = {isa = PBXFileReference; indentWidth = 4; lastKnownFileType = sourcecode.swift; path = OnboardingViewController.swift; sourceTree = ""; tabWidth = 4; }; 2A5BB7D42CDCA5C900E1C799 /* TermsOfServiceAgreeButtonView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TermsOfServiceAgreeButtonView.swift; sourceTree = ""; }; 2A5BB7D82CDCBA8400E1C799 /* OnboardingNicknameTextFieldView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnboardingNicknameTextFieldView.swift; sourceTree = ""; }; 2A5BB7DF2CDCBE7E00E1C799 /* OnboardingNicknameSettingViewReactor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnboardingNicknameSettingViewReactor.swift; sourceTree = ""; }; 2A5BB7E22CDCD97300E1C799 /* JoinRequest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JoinRequest.swift; sourceTree = ""; }; 2A5BB7E62CDCDC3600E1C799 /* NicknameValidationResponse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NicknameValidationResponse.swift; sourceTree = ""; }; - 2A5BB7F92CE277AF00E1C799 /* ProfileImageSettingViewReactor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileImageSettingViewReactor.swift; sourceTree = ""; }; + 2A5BB7F92CE277AF00E1C799 /* OnboardingProfileImageSettingViewReactor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnboardingProfileImageSettingViewReactor.swift; sourceTree = ""; }; 2A62805A2D084FEB00803BE9 /* GoogleService-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "GoogleService-Info.plist"; sourceTree = ""; }; 2A6280602D085C6200803BE9 /* SOOUM.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = SOOUM.entitlements; sourceTree = ""; }; 2A6280612D085C7600803BE9 /* SOOUM-Dev.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = "SOOUM-Dev.entitlements"; sourceTree = ""; }; @@ -603,7 +722,6 @@ 2A980B9F2D803EB1007DFA45 /* AnalyticsEventProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnalyticsEventProtocol.swift; sourceTree = ""; }; 2A980BA32D803EE2007DFA45 /* SOMEvent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SOMEvent.swift; sourceTree = ""; }; 2A980BA72D803F04007DFA45 /* GAManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GAManager.swift; sourceTree = ""; }; - 2ACBD4122CC944FB0057C013 /* UploadRequest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UploadRequest.swift; sourceTree = ""; }; 2ACBD4162CC963390057C013 /* DefaultCardImageResponse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DefaultCardImageResponse.swift; sourceTree = ""; }; 2ACBD4192CCA03790057C013 /* ImageURLWithName.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageURLWithName.swift; sourceTree = ""; }; 2ACBD41C2CCAB3490057C013 /* PresignedStorageResponse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PresignedStorageResponse.swift; sourceTree = ""; }; @@ -687,6 +805,12 @@ 3836ACB92C8F050D00A3C566 /* UILabel+Typography.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UILabel+Typography.swift"; sourceTree = ""; }; 38389B9B2CCCF98B006728AF /* AuthRequest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthRequest.swift; sourceTree = ""; }; 38389B9E2CCCFB7D006728AF /* AuthKeyChain.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthKeyChain.swift; sourceTree = ""; }; + 383EC6102E7A4F5E00EC2D1E /* AuthLocalDataSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthLocalDataSource.swift; sourceTree = ""; }; + 383EC6142E7A50E000EC2D1E /* AuthLocalDataSourceImpl.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthLocalDataSourceImpl.swift; sourceTree = ""; }; + 383EC6182E7A546B00EC2D1E /* BaseAssembler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BaseAssembler.swift; sourceTree = ""; }; + 383EC61B2E7A548600EC2D1E /* BaseDIContainer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BaseDIContainer.swift; sourceTree = ""; }; + 383EC61F2E7A564200EC2D1E /* AppAssembler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppAssembler.swift; sourceTree = ""; }; + 383EC6222E7A56CA00EC2D1E /* AppDIContainer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDIContainer.swift; sourceTree = ""; }; 38405CCA2CC611FD00612D1E /* BaseEmptyAndHeader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BaseEmptyAndHeader.swift; sourceTree = ""; }; 3843C1BD2D4FB778009283AC /* MockAlamofire.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockAlamofire.swift; sourceTree = ""; }; 3843C1BF2D4FC226009283AC /* MockNetworkManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockNetworkManager.swift; sourceTree = ""; }; @@ -767,13 +891,61 @@ 3886985E2D1984D600008600 /* NotificationViewReactor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationViewReactor.swift; sourceTree = ""; }; 388698612D1986B100008600 /* NotificationRequest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationRequest.swift; sourceTree = ""; }; 388698642D1998DB00008600 /* NotificationTabBarReactor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationTabBarReactor.swift; sourceTree = ""; }; + 388717692E7BD7AC00C6143B /* loading_indicator_lottie.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = loading_indicator_lottie.json; sourceTree = ""; }; + 3887176C2E7BDBA800C6143B /* NicknameResponse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NicknameResponse.swift; sourceTree = ""; }; 3887D0322CC5335200FB52E1 /* WriteCardViewReactor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WriteCardViewReactor.swift; sourceTree = ""; }; 3887D0352CC5335D00FB52E1 /* WriteCardView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WriteCardView.swift; sourceTree = ""; }; 3887D0382CC5504500FB52E1 /* UITextField+Typography.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UITextField+Typography.swift"; sourceTree = ""; }; + 38899E572E7936D50030F7CA /* SooumStyle_V2.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SooumStyle_V2.swift; sourceTree = ""; }; + 38899E5D2E7937DB0030F7CA /* NicknameValidateResponse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NicknameValidateResponse.swift; sourceTree = ""; }; + 38899E652E79395E0030F7CA /* CheckAvailableResponse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CheckAvailableResponse.swift; sourceTree = ""; }; + 38899E6A2E793AF70030F7CA /* CheckAvailable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CheckAvailable.swift; sourceTree = ""; }; + 38899E6D2E79400B0030F7CA /* ImageUrlInfoResponse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageUrlInfoResponse.swift; sourceTree = ""; }; + 38899E702E7940280030F7CA /* ImageUrlInfo.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageUrlInfo.swift; sourceTree = ""; }; + 38899E732E7943070030F7CA /* SignUpRequestInfo.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SignUpRequestInfo.swift; sourceTree = ""; }; + 38899E7C2E794B3D0030F7CA /* SignUpResponse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SignUpResponse.swift; sourceTree = ""; }; + 38899E822E794C330030F7CA /* LoginResponse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoginResponse.swift; sourceTree = ""; }; + 38899E852E794CE90030F7CA /* NetworkManager_FCM.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkManager_FCM.swift; sourceTree = ""; }; + 38899E882E794D5D0030F7CA /* NetworkManager_Version.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkManager_Version.swift; sourceTree = ""; }; + 38899E8B2E794E680030F7CA /* AppVersionStatusResponse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppVersionStatusResponse.swift; sourceTree = ""; }; + 38899E8E2E79511F0030F7CA /* KeyInfoResponse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyInfoResponse.swift; sourceTree = ""; }; + 38899E922E79518E0030F7CA /* CommonNotificationInfo.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CommonNotificationInfo.swift; sourceTree = ""; }; + 38899E952E7953300030F7CA /* NotificationInfoResponse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationInfoResponse.swift; sourceTree = ""; }; + 38899E982E7954670030F7CA /* BlockedNotificationInfoResponse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BlockedNotificationInfoResponse.swift; sourceTree = ""; }; + 38899E9B2E7954D70030F7CA /* DeleteNotificationInfoResponse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeleteNotificationInfoResponse.swift; sourceTree = ""; }; + 38899EA22E799B190030F7CA /* AppVersionRemoteDataSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppVersionRemoteDataSource.swift; sourceTree = ""; }; + 38899EA52E799BD10030F7CA /* AppVersionRemoteDataSourceImpl.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppVersionRemoteDataSourceImpl.swift; sourceTree = ""; }; + 38899EA82E799C5D0030F7CA /* VersionRequest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VersionRequest.swift; sourceTree = ""; }; + 38899EAC2E79A0990030F7CA /* UserRequest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserRequest.swift; sourceTree = ""; }; + 3889A2422E79AD600030F7CA /* AppVersionRepository.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppVersionRepository.swift; sourceTree = ""; }; + 3889A2452E79ADBD0030F7CA /* AppVersionRepositoryImpl.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppVersionRepositoryImpl.swift; sourceTree = ""; }; + 3889A2492E79AE900030F7CA /* AppVersionUseCase.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppVersionUseCase.swift; sourceTree = ""; }; + 3889A24C2E79AEAD0030F7CA /* AppVersionUseCaseImpl.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppVersionUseCaseImpl.swift; sourceTree = ""; }; + 3889A24F2E79B3210030F7CA /* UserRemoteDataSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserRemoteDataSource.swift; sourceTree = ""; }; + 3889A2552E79BA0F0030F7CA /* UserRemoteDataSourceImpl.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserRemoteDataSourceImpl.swift; sourceTree = ""; }; + 3889A25B2E79BB2F0030F7CA /* UserRepository.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserRepository.swift; sourceTree = ""; }; + 3889A2612E79BB540030F7CA /* UserRepositoryImpl.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserRepositoryImpl.swift; sourceTree = ""; }; + 3889A2642E79BBC00030F7CA /* UserUseCase.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserUseCase.swift; sourceTree = ""; }; + 3889A2672E79BC840030F7CA /* UserUseCaseImpl.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserUseCaseImpl.swift; sourceTree = ""; }; + 3889A26A2E79BD410030F7CA /* AuthRemoteDataSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthRemoteDataSource.swift; sourceTree = ""; }; + 3889A26D2E79BE970030F7CA /* AuthRemoteDataSourceImpl.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthRemoteDataSourceImpl.swift; sourceTree = ""; }; + 3889A2702E79C0370030F7CA /* AuthRepository.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthRepository.swift; sourceTree = ""; }; + 3889A2732E79C1D30030F7CA /* NotificationRemoteDataSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationRemoteDataSource.swift; sourceTree = ""; }; + 3889A2762E79C2980030F7CA /* NotificationRemoteDataSoruceImpl.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationRemoteDataSoruceImpl.swift; sourceTree = ""; }; + 3889A27C2E79C5670030F7CA /* ToeknResponse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ToeknResponse.swift; sourceTree = ""; }; + 3889A27F2E79D0230030F7CA /* Token.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Token.swift; sourceTree = ""; }; + 3889A2822E79D7CE0030F7CA /* AuthRepositoryImpl.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthRepositoryImpl.swift; sourceTree = ""; }; + 3889A2852E79D8060030F7CA /* AuthUseCase.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthUseCase.swift; sourceTree = ""; }; + 3889A2882E79D81E0030F7CA /* AuthUseCaseImpl.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthUseCaseImpl.swift; sourceTree = ""; }; + 3889A28B2E79D8650030F7CA /* NotificationRepository.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationRepository.swift; sourceTree = ""; }; + 3889A28E2E79D8800030F7CA /* NotificationRepositoryImpl.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationRepositoryImpl.swift; sourceTree = ""; }; + 3889A2912E79D8F40030F7CA /* NotificationUseCase.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationUseCase.swift; sourceTree = ""; }; + 3889A2942E79D9200030F7CA /* NotificationUseCaseImpl.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationUseCaseImpl.swift; sourceTree = ""; }; 388A2D2C2D00A45800E2F2F0 /* writtenCardResponse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = writtenCardResponse.swift; sourceTree = ""; }; 388A2D2F2D00D6A100E2F2F0 /* FollowViewReactor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FollowViewReactor.swift; sourceTree = ""; }; 388A2D322D00D7BF00E2F2F0 /* UpdateProfileViewReactor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UpdateProfileViewReactor.swift; sourceTree = ""; }; 388C96352CCE41700061C598 /* AuthInfo.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthInfo.swift; sourceTree = ""; }; + 388D8ADE2E73E6130044BA79 /* SwiftEntryKit.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwiftEntryKit.swift; sourceTree = ""; }; 388DA0FA2C8F521000A9DD56 /* FontContainer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FontContainer.swift; sourceTree = ""; }; 388DA0FD2C8F526C00A9DD56 /* UIFont.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIFont.swift; sourceTree = ""; }; 388DA1002C8F538400A9DD56 /* PretendardVariable.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = PretendardVariable.ttf; sourceTree = ""; }; @@ -788,17 +960,24 @@ 389EF81D2D2F469B00E053AE /* CocoaLumberjack.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CocoaLumberjack.swift; sourceTree = ""; }; 38A5D1532C8CB11E00B68363 /* UIImage+SOOUM.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIImage+SOOUM.swift"; sourceTree = ""; }; 38A627162CECC5A800C37A03 /* SOMTagsLayoutConfigure.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SOMTagsLayoutConfigure.swift; sourceTree = ""; }; + 38A721942E73E7010071E1D8 /* View+SwiftEntryKit.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "View+SwiftEntryKit.swift"; sourceTree = ""; }; + 38A721982E73EA610071E1D8 /* SelectProfileBottomFloatView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SelectProfileBottomFloatView.swift; sourceTree = ""; }; 38AA00012CAD1BCC002C5F1E /* LikeAndCommentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LikeAndCommentView.swift; sourceTree = ""; }; 38AA00052CAD96E3002C5F1E /* MoreBottomSheetViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MoreBottomSheetViewController.swift; sourceTree = ""; }; 38AA66212D3AA86F00B3F6B2 /* SOMDialogAction.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SOMDialogAction.swift; sourceTree = ""; }; - 38AA66252D3AC3F500B3F6B2 /* DialogMessageView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DialogMessageView.swift; sourceTree = ""; }; 38AE565B2D048B4800CAA431 /* SOMDialogViewController+Show.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "SOMDialogViewController+Show.swift"; sourceTree = ""; }; + 38AE77D32E7457F400B6FD13 /* OnboardingCompletedViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnboardingCompletedViewController.swift; sourceTree = ""; }; + 38AE77D62E7459EA00B6FD13 /* OnboardingCompletedViewReactor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnboardingCompletedViewReactor.swift; sourceTree = ""; }; + 38AE77DA2E745FF700B6FD13 /* EnterMemberTransferTextFieldView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EnterMemberTransferTextFieldView.swift; sourceTree = ""; }; + 38AE77DD2E7465E600B6FD13 /* EnterMemberTransferTextFieldView+Rx.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "EnterMemberTransferTextFieldView+Rx.swift"; sourceTree = ""; }; 38B543DE2D46171300DDF2C5 /* ManagerConfiguration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ManagerConfiguration.swift; sourceTree = ""; }; 38B543E12D46179500DDF2C5 /* AuthManagerConfiguration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthManagerConfiguration.swift; sourceTree = ""; }; 38B543E42D4617CB00DDF2C5 /* PushManagerConfiguration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PushManagerConfiguration.swift; sourceTree = ""; }; 38B543E72D4617EA00DDF2C5 /* NetworkManagerConfiguration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkManagerConfiguration.swift; sourceTree = ""; }; 38B543EA2D461B1A00DDF2C5 /* LocationManagerConfigruation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocationManagerConfigruation.swift; sourceTree = ""; }; 38B543ED2D46506300DDF2C5 /* ManagerType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ManagerType.swift; sourceTree = ""; }; + 38B65E782E72A29100DF6919 /* OnboardingNumberingView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnboardingNumberingView.swift; sourceTree = ""; }; + 38B65E7B2E72ADB500DF6919 /* TermsOfServiceAgreeButtonView+Rx.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "TermsOfServiceAgreeButtonView+Rx.swift"; sourceTree = ""; }; 38B6AACC2CA410D800CE6DB6 /* MainTabBarController.swift */ = {isa = PBXFileReference; indentWidth = 4; lastKnownFileType = sourcecode.swift; path = MainTabBarController.swift; sourceTree = ""; tabWidth = 4; }; 38B6AAD72CA424AE00CE6DB6 /* MoveTopButtonView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MoveTopButtonView.swift; sourceTree = ""; }; 38B6AADA2CA4740B00CE6DB6 /* LaunchScreenViewReactor.swift */ = {isa = PBXFileReference; indentWidth = 4; lastKnownFileType = sourcecode.swift; path = LaunchScreenViewReactor.swift; sourceTree = ""; tabWidth = 4; }; @@ -825,6 +1004,7 @@ 38D3CB142CC2362B001EC280 /* Hakgyoansim-Bold.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "Hakgyoansim-Bold.ttf"; sourceTree = ""; }; 38D3CB152CC2362B001EC280 /* Hakgyoansim-Light.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "Hakgyoansim-Light.ttf"; sourceTree = ""; }; 38D488C92D0C557300F2D38D /* SOMButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SOMButton.swift; sourceTree = ""; }; + 38D522672E742F550044911B /* SOMLoadingIndicatorView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SOMLoadingIndicatorView.swift; sourceTree = ""; }; 38D5637A2D16D72D006265AA /* SOMSwipeTabBar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SOMSwipeTabBar.swift; sourceTree = ""; }; 38D5637D2D17152F006265AA /* SOMSwipeTabBarDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SOMSwipeTabBarDelegate.swift; sourceTree = ""; }; 38D563832D1719B1006265AA /* SOMSwipeTabBarItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SOMSwipeTabBarItem.swift; sourceTree = ""; }; @@ -927,6 +1107,7 @@ 2A5BB7BB2CDB86DD00E1C799 /* TermsOfService */, 2A5BB7C72CDBA4F400E1C799 /* NicknameSetting */, 2A5BB7CB2CDBB7B200E1C799 /* ProfileImageSetting */, + 38AE77D22E7457E700B6FD13 /* Completed */, 2A5BB7BC2CDB86E500E1C799 /* Views */, ); path = Onboarding; @@ -945,6 +1126,7 @@ 2A5BB7BC2CDB86E500E1C799 /* Views */ = { isa = PBXGroup; children = ( + 38B65E782E72A29100DF6919 /* OnboardingNumberingView.swift */, 2A5BB7BD2CDB870000E1C799 /* OnboardingGuideMessageView.swift */, ); path = Views; @@ -963,8 +1145,9 @@ 2A5BB7CB2CDBB7B200E1C799 /* ProfileImageSetting */ = { isa = PBXGroup; children = ( - 2A5BB7CC2CDBB7D100E1C799 /* ProfileImageSettingViewController.swift */, - 2A5BB7F92CE277AF00E1C799 /* ProfileImageSettingViewReactor.swift */, + 2A5BB7CC2CDBB7D100E1C799 /* OnboardingProfileImageSettingViewController.swift */, + 2A5BB7F92CE277AF00E1C799 /* OnboardingProfileImageSettingViewReactor.swift */, + 38A721972E73EA5B0071E1D8 /* Views */, ); path = ProfileImageSetting; sourceTree = ""; @@ -974,7 +1157,6 @@ children = ( 2A5BB7D02CDC7ADC00E1C799 /* OnboardingViewController.swift */, 38E9CE122D37711600E85A2D /* OnboardingViewReactor.swift */, - 38AA66242D3AC3E400B3F6B2 /* Views */, ); path = Onboarding; sourceTree = ""; @@ -982,6 +1164,7 @@ 2A5BB7D32CDCA5BE00E1C799 /* Views */ = { isa = PBXGroup; children = ( + 38B65E7B2E72ADB500DF6919 /* TermsOfServiceAgreeButtonView+Rx.swift */, 2A5BB7D42CDCA5C900E1C799 /* TermsOfServiceAgreeButtonView.swift */, 3816E2362D3BEE7E004CC196 /* TermsOfServiceCellView.swift */, 3816E2392D3BF402004CC196 /* TermsOfServiceCellView+Rx.swift */, @@ -1288,6 +1471,7 @@ children = ( 3803CF812D017DB800FD90DB /* EnterMemberTransferViewController.swift */, 3803CF842D017DC700FD90DB /* EnterMemberTransferViewReactor.swift */, + 38AE77D92E745FF100B6FD13 /* Views */, ); path = Enter; sourceTree = ""; @@ -1386,6 +1570,7 @@ isa = PBXGroup; children = ( 385620F42CA19E8600E0AB5A /* Alamofire */, + 388D8ADD2E73E60B0044BA79 /* SwiftEntryKit */, 3836ACB22C8F043500A3C566 /* Typography */, 38D5CE0A2CBCE8CA0054AB9A /* SimpleDefaults.swift */, 38389B9E2CCCFB7D006728AF /* AuthKeyChain.swift */, @@ -1415,12 +1600,37 @@ 2A44A4362CAC227300DC463E /* BaseAuthResponse.swift */, 2A44A4292CAC09AE00DC463E /* RSAKeyResponse.swift */, 2A44A42C2CAC14C800DC463E /* SignInResponse.swift */, - 2A44A4332CAC21A500DC463E /* SignUpResponse.swift */, 3816C05B2CCDDF3D00C8688C /* ReAuthenticationResponse.swift */, ); path = Auth; sourceTree = ""; }; + 383EC6132E7A50D900EC2D1E /* Interfaces */ = { + isa = PBXGroup; + children = ( + 383EC6102E7A4F5E00EC2D1E /* AuthLocalDataSource.swift */, + ); + path = Interfaces; + sourceTree = ""; + }; + 383EC6172E7A544B00EC2D1E /* DIContainer */ = { + isa = PBXGroup; + children = ( + 383EC61B2E7A548600EC2D1E /* BaseDIContainer.swift */, + 383EC6182E7A546B00EC2D1E /* BaseAssembler.swift */, + ); + path = DIContainer; + sourceTree = ""; + }; + 383EC61E2E7A563600EC2D1E /* Dependencies */ = { + isa = PBXGroup; + children = ( + 383EC6222E7A56CA00EC2D1E /* AppDIContainer.swift */, + 383EC61F2E7A564200EC2D1E /* AppAssembler.swift */, + ); + path = Dependencies; + sourceTree = ""; + }; 3843C1BC2D4FB399009283AC /* Network */ = { isa = PBXGroup; children = ( @@ -1489,6 +1699,8 @@ isa = PBXGroup; children = ( 3800575B2D9C12CB00E58A19 /* DefinedError.swift */, + 38899E882E794D5D0030F7CA /* NetworkManager_Version.swift */, + 38899E852E794CE90030F7CA /* NetworkManager_FCM.swift */, 385620EE2CA19C9500E0AB5A /* NetworkManager.swift */, 38B543E72D4617EA00DDF2C5 /* NetworkManagerConfiguration.swift */, 38F88EBC2D2C1E19002AD7A8 /* Models */, @@ -1693,7 +1905,8 @@ 388371F62C8C8E7F004212EB /* DesignSystem */, 387FBB032C87036D00A5E139 /* App */, 38FDC2B42C9E745B00C094C2 /* Base */, - 385620EC2CA19C0D00E0AB5A /* Managers */, + 38899E5C2E79378A0030F7CA /* Data */, + 38899E682E793AD10030F7CA /* Domain */, 38B6AAE42CA47E8D00CE6DB6 /* Models */, 385071972CA2956D00A7905A /* Presentations */, 388371FE2C8C8FAC004212EB /* Extensions */, @@ -1706,6 +1919,7 @@ 387FBB032C87036D00A5E139 /* App */ = { isa = PBXGroup; children = ( + 383EC61E2E7A563600EC2D1E /* Dependencies */, 387FBAEF2C8702C100A5E139 /* AppDelegate.swift */, 387FBAF12C8702C100A5E139 /* SceneDelegate.swift */, ); @@ -1715,13 +1929,12 @@ 387FBB052C87038F00A5E139 /* Resources */ = { isa = PBXGroup; children = ( + 388717692E7BD7AC00C6143B /* loading_indicator_lottie.json */, 387FBAF82C8702C200A5E139 /* Assets.xcassets */, 2A6280602D085C6200803BE9 /* SOOUM.entitlements */, 387FBAFA2C8702C200A5E139 /* LaunchScreen.storyboard */, 2A6280612D085C7600803BE9 /* SOOUM-Dev.entitlements */, - 38D3CB142CC2362B001EC280 /* Hakgyoansim-Bold.ttf */, - 38D3CB152CC2362B001EC280 /* Hakgyoansim-Light.ttf */, - 388DA1002C8F538400A9DD56 /* PretendardVariable.ttf */, + 3880DE182E785C90000B12E2 /* Font */, 38B6AAE82CA47F5C00CE6DB6 /* Alamofire */, 387FBB152C87044700A5E139 /* Develop */, 387FBB062C8703F600A5E139 /* Production */, @@ -1773,6 +1986,16 @@ path = SOMTags; sourceTree = ""; }; + 3880DE182E785C90000B12E2 /* Font */ = { + isa = PBXGroup; + children = ( + 38D3CB142CC2362B001EC280 /* Hakgyoansim-Bold.ttf */, + 38D3CB152CC2362B001EC280 /* Hakgyoansim-Light.ttf */, + 388DA1002C8F538400A9DD56 /* PretendardVariable.ttf */, + ); + path = Font; + sourceTree = ""; + }; 38816D9C2D004A3000EB87D6 /* UpdateProfile */ = { isa = PBXGroup; children = ( @@ -1803,7 +2026,7 @@ 388371F72C8C8E8C004212EB /* Foundations */ = { isa = PBXGroup; children = ( - 388371F82C8C8EB1004212EB /* SooumStyle.swift */, + 38899E562E7936CD0030F7CA /* Style */, 388DA1032C8F545E00A9DD56 /* Typography+SOOUM.swift */, 388371FB2C8C8F11004212EB /* UIColor+SOOUM.swift */, 38A5D1532C8CB11E00B68363 /* UIImage+SOOUM.swift */, @@ -1858,6 +2081,174 @@ path = Views; sourceTree = ""; }; + 38899E562E7936CD0030F7CA /* Style */ = { + isa = PBXGroup; + children = ( + 38899E572E7936D50030F7CA /* SooumStyle_V2.swift */, + 388371F82C8C8EB1004212EB /* SooumStyle.swift */, + ); + path = Style; + sourceTree = ""; + }; + 38899E5A2E79374E0030F7CA /* Repositories */ = { + isa = PBXGroup; + children = ( + 3889A28E2E79D8800030F7CA /* NotificationRepositoryImpl.swift */, + 3889A2822E79D7CE0030F7CA /* AuthRepositoryImpl.swift */, + 3889A2612E79BB540030F7CA /* UserRepositoryImpl.swift */, + 3889A2452E79ADBD0030F7CA /* AppVersionRepositoryImpl.swift */, + 38899E9F2E799A7E0030F7CA /* Remotes */, + 38899E9E2E799A740030F7CA /* Locals */, + ); + path = Repositories; + sourceTree = ""; + }; + 38899E5B2E7937660030F7CA /* Models */ = { + isa = PBXGroup; + children = ( + 38899E632E7938CD0030F7CA /* Responses */, + ); + path = Models; + sourceTree = ""; + }; + 38899E5C2E79378A0030F7CA /* Data */ = { + isa = PBXGroup; + children = ( + 38899E5B2E7937660030F7CA /* Models */, + 385620EC2CA19C0D00E0AB5A /* Managers */, + 38899E5A2E79374E0030F7CA /* Repositories */, + ); + path = Data; + sourceTree = ""; + }; + 38899E632E7938CD0030F7CA /* Responses */ = { + isa = PBXGroup; + children = ( + 3887176C2E7BDBA800C6143B /* NicknameResponse.swift */, + 3889A27C2E79C5670030F7CA /* ToeknResponse.swift */, + 38899E9B2E7954D70030F7CA /* DeleteNotificationInfoResponse.swift */, + 38899E982E7954670030F7CA /* BlockedNotificationInfoResponse.swift */, + 38899E952E7953300030F7CA /* NotificationInfoResponse.swift */, + 38899E8E2E79511F0030F7CA /* KeyInfoResponse.swift */, + 38899E8B2E794E680030F7CA /* AppVersionStatusResponse.swift */, + 38899E822E794C330030F7CA /* LoginResponse.swift */, + 38899E7C2E794B3D0030F7CA /* SignUpResponse.swift */, + 38899E6D2E79400B0030F7CA /* ImageUrlInfoResponse.swift */, + 38899E652E79395E0030F7CA /* CheckAvailableResponse.swift */, + 38899E5D2E7937DB0030F7CA /* NicknameValidateResponse.swift */, + ); + path = Responses; + sourceTree = ""; + }; + 38899E682E793AD10030F7CA /* Domain */ = { + isa = PBXGroup; + children = ( + 3889A2412E79AD3A0030F7CA /* Repositories */, + 3889A2402E79AD320030F7CA /* UseCases */, + 38899E692E793AEA0030F7CA /* Models */, + ); + path = Domain; + sourceTree = ""; + }; + 38899E692E793AEA0030F7CA /* Models */ = { + isa = PBXGroup; + children = ( + 3889A27F2E79D0230030F7CA /* Token.swift */, + 38899E922E79518E0030F7CA /* CommonNotificationInfo.swift */, + 38F88EBD2D2C1E22002AD7A8 /* Version.swift */, + 38899E732E7943070030F7CA /* SignUpRequestInfo.swift */, + 38899E702E7940280030F7CA /* ImageUrlInfo.swift */, + 38899E6A2E793AF70030F7CA /* CheckAvailable.swift */, + ); + path = Models; + sourceTree = ""; + }; + 38899E9E2E799A740030F7CA /* Locals */ = { + isa = PBXGroup; + children = ( + 383EC6142E7A50E000EC2D1E /* AuthLocalDataSourceImpl.swift */, + 383EC6132E7A50D900EC2D1E /* Interfaces */, + ); + path = Locals; + sourceTree = ""; + }; + 38899E9F2E799A7E0030F7CA /* Remotes */ = { + isa = PBXGroup; + children = ( + 3889A2762E79C2980030F7CA /* NotificationRemoteDataSoruceImpl.swift */, + 3889A26D2E79BE970030F7CA /* AuthRemoteDataSourceImpl.swift */, + 3889A2552E79BA0F0030F7CA /* UserRemoteDataSourceImpl.swift */, + 38899EA52E799BD10030F7CA /* AppVersionRemoteDataSourceImpl.swift */, + 38899EA02E799AA30030F7CA /* Interfaces */, + ); + path = Remotes; + sourceTree = ""; + }; + 38899EA02E799AA30030F7CA /* Interfaces */ = { + isa = PBXGroup; + children = ( + 3889A2732E79C1D30030F7CA /* NotificationRemoteDataSource.swift */, + 3889A26A2E79BD410030F7CA /* AuthRemoteDataSource.swift */, + 3889A24F2E79B3210030F7CA /* UserRemoteDataSource.swift */, + 38899EA22E799B190030F7CA /* AppVersionRemoteDataSource.swift */, + ); + path = Interfaces; + sourceTree = ""; + }; + 38899EAB2E799E360030F7CA /* V2 */ = { + isa = PBXGroup; + children = ( + 38389B9B2CCCF98B006728AF /* AuthRequest.swift */, + 388698612D1986B100008600 /* NotificationRequest.swift */, + 38899EAC2E79A0990030F7CA /* UserRequest.swift */, + 38899EA82E799C5D0030F7CA /* VersionRequest.swift */, + ); + path = V2; + sourceTree = ""; + }; + 3889A2402E79AD320030F7CA /* UseCases */ = { + isa = PBXGroup; + children = ( + 3889A2942E79D9200030F7CA /* NotificationUseCaseImpl.swift */, + 3889A2882E79D81E0030F7CA /* AuthUseCaseImpl.swift */, + 3889A2672E79BC840030F7CA /* UserUseCaseImpl.swift */, + 3889A24C2E79AEAD0030F7CA /* AppVersionUseCaseImpl.swift */, + 3889A2482E79AE870030F7CA /* Interfaces */, + ); + path = UseCases; + sourceTree = ""; + }; + 3889A2412E79AD3A0030F7CA /* Repositories */ = { + isa = PBXGroup; + children = ( + 3889A28B2E79D8650030F7CA /* NotificationRepository.swift */, + 3889A2702E79C0370030F7CA /* AuthRepository.swift */, + 3889A25B2E79BB2F0030F7CA /* UserRepository.swift */, + 3889A2422E79AD600030F7CA /* AppVersionRepository.swift */, + ); + path = Repositories; + sourceTree = ""; + }; + 3889A2482E79AE870030F7CA /* Interfaces */ = { + isa = PBXGroup; + children = ( + 3889A2912E79D8F40030F7CA /* NotificationUseCase.swift */, + 3889A2852E79D8060030F7CA /* AuthUseCase.swift */, + 3889A2642E79BBC00030F7CA /* UserUseCase.swift */, + 3889A2492E79AE900030F7CA /* AppVersionUseCase.swift */, + ); + path = Interfaces; + sourceTree = ""; + }; + 388D8ADD2E73E60B0044BA79 /* SwiftEntryKit */ = { + isa = PBXGroup; + children = ( + 38A721942E73E7010071E1D8 /* View+SwiftEntryKit.swift */, + 388D8ADE2E73E6130044BA79 /* SwiftEntryKit.swift */, + ); + path = SwiftEntryKit; + sourceTree = ""; + }; 389596A12D15A4CB000662B6 /* Notification */ = { isa = PBXGroup; children = ( @@ -1878,6 +2269,14 @@ path = Log; sourceTree = ""; }; + 38A721972E73EA5B0071E1D8 /* Views */ = { + isa = PBXGroup; + children = ( + 38A721982E73EA610071E1D8 /* SelectProfileBottomFloatView.swift */, + ); + path = Views; + sourceTree = ""; + }; 38AA00002CAD1BB2002C5F1E /* Views */ = { isa = PBXGroup; children = ( @@ -1904,14 +2303,6 @@ path = Intro; sourceTree = ""; }; - 38AA66242D3AC3E400B3F6B2 /* Views */ = { - isa = PBXGroup; - children = ( - 38AA66252D3AC3F500B3F6B2 /* DialogMessageView.swift */, - ); - path = Views; - sourceTree = ""; - }; 38AE56572D0489E500CAA431 /* SOMDialogController */ = { isa = PBXGroup; children = ( @@ -1922,6 +2313,24 @@ path = SOMDialogController; sourceTree = ""; }; + 38AE77D22E7457E700B6FD13 /* Completed */ = { + isa = PBXGroup; + children = ( + 38AE77D32E7457F400B6FD13 /* OnboardingCompletedViewController.swift */, + 38AE77D62E7459EA00B6FD13 /* OnboardingCompletedViewReactor.swift */, + ); + path = Completed; + sourceTree = ""; + }; + 38AE77D92E745FF100B6FD13 /* Views */ = { + isa = PBXGroup; + children = ( + 38AE77DD2E7465E600B6FD13 /* EnterMemberTransferTextFieldView+Rx.swift */, + 38AE77DA2E745FF700B6FD13 /* EnterMemberTransferTextFieldView.swift */, + ); + path = Views; + sourceTree = ""; + }; 38B6AACB2CA410C000CE6DB6 /* Main */ = { isa = PBXGroup; children = ( @@ -1995,16 +2404,14 @@ 38B6AAE92CA47F6200CE6DB6 /* Request */ = { isa = PBXGroup; children = ( + 38899EAB2E799E360030F7CA /* V2 */, 2A5ABA332D464E0B00BF6C9B /* ConfigureRequest.swift */, 384972A02CA4DEC00012FCA1 /* CardRequest.swift */, 2AE6B1592CBEAEC000FA5C3C /* ReportRequest.swift */, - 38389B9B2CCCF98B006728AF /* AuthRequest.swift */, - 2ACBD4122CC944FB0057C013 /* UploadRequest.swift */, 2A5BB7E22CDCD97300E1C799 /* JoinRequest.swift */, 3878D04D2CFFC5F300F9522F /* ProfileRequest.swift */, 3803CF6B2D0156FC00FD90DB /* SettingsRequest.swift */, 2AFD054B2CFF76CB007C84AD /* TagRequest.swift */, - 388698612D1986B100008600 /* NotificationRequest.swift */, ); path = Request; sourceTree = ""; @@ -2072,6 +2479,7 @@ 38D488C92D0C557300F2D38D /* SOMButton.swift */, 38773E7B2CB3ACB2004815CD /* SOMRefreshControl.swift */, 38D055C22CD862FE00E75590 /* SOMActivityIndicatorView.swift */, + 38D522672E742F550044911B /* SOMLoadingIndicatorView.swift */, ); path = Components; sourceTree = ""; @@ -2217,7 +2625,6 @@ 38F88EBC2D2C1E19002AD7A8 /* Models */ = { isa = PBXGroup; children = ( - 38F88EBD2D2C1E22002AD7A8 /* Version.swift */, 38E9CE0F2D376E0E00E85A2D /* PushTokenSet.swift */, ); path = Models; @@ -2226,6 +2633,7 @@ 38FDC2B42C9E745B00C094C2 /* Base */ = { isa = PBXGroup; children = ( + 383EC6172E7A544B00EC2D1E /* DIContainer */, 38FDC2B52C9E746B00C094C2 /* BaseViewController.swift */, 38FDC2C62C9E764300C094C2 /* BaseNavigationViewController.swift */, ); @@ -2346,6 +2754,7 @@ 385441962C870544004E2BB0 /* Base in Resources */, 38D3CB192CC2362B001EC280 /* Hakgyoansim-Light.ttf in Resources */, 2A62805B2D084FEB00803BE9 /* GoogleService-Info.plist in Resources */, + 3887176A2E7BD7AC00C6143B /* loading_indicator_lottie.json in Resources */, 388DA1022C8F538400A9DD56 /* PretendardVariable.ttf in Resources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -2366,6 +2775,7 @@ 38D3CB162CC2362B001EC280 /* Hakgyoansim-Bold.ttf in Resources */, 387FBAFC2C8702C200A5E139 /* Base in Resources */, 38D3CB182CC2362B001EC280 /* Hakgyoansim-Light.ttf in Resources */, + 3887176B2E7BD7AC00C6143B /* loading_indicator_lottie.json in Resources */, 388DA1012C8F538400A9DD56 /* PretendardVariable.ttf in Resources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -2546,16 +2956,19 @@ 385620F72CA19EA900E0AB5A /* Alamofire_constants.swift in Sources */, 3817016F2CD882C2005FC220 /* TimeoutInterceptor.swift in Sources */, 38AE565D2D048B4800CAA431 /* SOMDialogViewController+Show.swift in Sources */, + 3889A2682E79BC880030F7CA /* UserUseCaseImpl.swift in Sources */, 3878D0762CFFE01500F9522F /* SettingScrollViewHeader.swift in Sources */, 3878D08D2CFFF0BF00F9522F /* AnnouncementViewControler.swift in Sources */, 38601E1C2D313A8200A465A9 /* SOMNavigationBar+Rx.swift in Sources */, 38FD4DB22D034C1700BF5FF1 /* MyFollowingViewCell.swift in Sources */, + 383EC61A2E7A547900EC2D1E /* BaseAssembler.swift in Sources */, 38CC49832CDE3854007A0145 /* SOMPresentationController.swift in Sources */, 2AE6B1672CBFB81000FA5C3C /* UploadCardBottomSheetViewReactor.swift in Sources */, 3836ACB52C8F045300A3C566 /* Typography.swift in Sources */, 38389BA02CCCFB7D006728AF /* AuthKeyChain.swift in Sources */, 2A048E852C9BE01300FFD485 /* SOMLocationFilterCollectionViewCell.swift in Sources */, 2A5ABA342D464E0B00BF6C9B /* ConfigureRequest.swift in Sources */, + 38899E902E7951200030F7CA /* KeyInfoResponse.swift in Sources */, 388371FA2C8C8EB1004212EB /* SooumStyle.swift in Sources */, 38F720B42CD4F15900DF32B5 /* LatestCardResponse.swift in Sources */, 3803CF6A2D0156BA00FD90DB /* SettingsViewReactor.swift in Sources */, @@ -2566,6 +2979,8 @@ 3816E23B2D3BF402004CC196 /* TermsOfServiceCellView+Rx.swift in Sources */, 2AFD054A2CFF7687007C84AD /* RecommendTagsResponse.swift in Sources */, 38773E7D2CB3ACB2004815CD /* SOMRefreshControl.swift in Sources */, + 3889A2572E79BA160030F7CA /* UserRemoteDataSourceImpl.swift in Sources */, + 3889A24D2E79AEB30030F7CA /* AppVersionUseCaseImpl.swift in Sources */, 38CC49862CDE3885007A0145 /* SOMTransitioningDelegate.swift in Sources */, 388DA0FC2C8F521300A9DD56 /* FontContainer.swift in Sources */, 384972A12CA4DEC00012FCA1 /* CardRequest.swift in Sources */, @@ -2581,27 +2996,33 @@ 3878D05D2CFFD10D00F9522F /* FollowingResponse.swift in Sources */, 38D6F17D2CC2406700E11530 /* WriteCardViewController.swift in Sources */, 385053592C92DD2300C80B02 /* SOMTabBarController.swift in Sources */, + 383EC61D2E7A548E00EC2D1E /* BaseDIContainer.swift in Sources */, 385009C32D363525007175A1 /* FilterNil.swift in Sources */, 3816C05D2CCDDF3D00C8688C /* ReAuthenticationResponse.swift in Sources */, 38FDC2B72C9E746B00C094C2 /* BaseViewController.swift in Sources */, 38CC49892CDE3972007A0145 /* SOMPresentationController+Show.swift in Sources */, 38F70E5C2D1905D000B33C9D /* MainHomeTabBarController.swift in Sources */, 38E7FBF02D3CF6BC00A359CD /* SOMDialogAction.swift in Sources */, - 38AA66272D3AC3F500B3F6B2 /* DialogMessageView.swift in Sources */, 3878D07E2CFFE6E500F9522F /* IssueMemberTransferViewController.swift in Sources */, 3878B8632D0DC8BD00B3B128 /* UIViewController+Toast.swift in Sources */, - 2A44A4352CAC21A500DC463E /* SignUpResponse.swift in Sources */, + 38899E742E79430A0030F7CA /* SignUpRequestInfo.swift in Sources */, 38E9CE112D376E0E00E85A2D /* PushTokenSet.swift in Sources */, 38F70E702D191DFB00B33C9D /* MainHomePopularViewReactor.swift in Sources */, 3878D0982CFFF2B800F9522F /* CommentHistroyViewController.swift in Sources */, + 38899E6F2E79400C0030F7CA /* ImageUrlInfoResponse.swift in Sources */, 38F720AE2CD4F15900DF32B5 /* DetailCardResponse.swift in Sources */, + 3889A2772E79C29F0030F7CA /* NotificationRemoteDataSoruceImpl.swift in Sources */, 2AFD056A2D03264C007C84AD /* AddFavoriteTagResponse.swift in Sources */, 2AFD05532D007F2F007C84AD /* TagSearchViewReactor.swift in Sources */, + 38899E9C2E7954D90030F7CA /* DeleteNotificationInfoResponse.swift in Sources */, 2A44A42B2CAC09AE00DC463E /* RSAKeyResponse.swift in Sources */, + 38899E8C2E794E690030F7CA /* AppVersionStatusResponse.swift in Sources */, + 38A721962E73E7140071E1D8 /* View+SwiftEntryKit.swift in Sources */, 38572CD92D2230C900B07C69 /* NotificationAllowResponse.swift in Sources */, 382E15402D15AF4F0097B09C /* CommentHistoryInNotiResponse.swift in Sources */, 388698632D1986B100008600 /* NotificationRequest.swift in Sources */, 2A980BA52D803EEA007DFA45 /* SOMEvent.swift in Sources */, + 38B65E7C2E72ADB900DF6919 /* TermsOfServiceAgreeButtonView+Rx.swift in Sources */, 38C2D4152CFEA9CC00CEA092 /* MyProfileViewCell.swift in Sources */, 38F720B22CD4F15900DF32B5 /* distanceCardResponse.swift in Sources */, 38FD4DB52D034F6600BF5FF1 /* MyFollowerViewCell.swift in Sources */, @@ -2613,6 +3034,7 @@ 2AFD05642D00A1E1007C84AD /* TagDetailCardResponse.swift in Sources */, 3836ACB82C8F04CD00A3C566 /* UILabel+Observer.swift in Sources */, 2A5BB7E82CDCDC3600E1C799 /* NicknameValidationResponse.swift in Sources */, + 383EC6122E7A4F6B00EC2D1E /* AuthLocalDataSource.swift in Sources */, 2AFF95622CF33A3900CBFB12 /* FavoriteTagView.swift in Sources */, 2A032EFE2CE517DD008326C0 /* OnboardingTermsOfServiceViewReactor.swift in Sources */, 388C96372CCE41700061C598 /* AuthInfo.swift in Sources */, @@ -2622,16 +3044,22 @@ 2A5BB7BF2CDB870000E1C799 /* OnboardingGuideMessageView.swift in Sources */, 388A2D2E2D00A45800E2F2F0 /* writtenCardResponse.swift in Sources */, 3878D07A2CFFE1E800F9522F /* ResignViewController.swift in Sources */, + 38899EAD2E79A09B0030F7CA /* UserRequest.swift in Sources */, 38738D4C2D2FDCC300C37574 /* WithoutReadNotisCountResponse.swift in Sources */, + 3889A2722E79C03B0030F7CA /* AuthRepository.swift in Sources */, 38A627182CECC5A800C37A03 /* SOMTagsLayoutConfigure.swift in Sources */, + 38AE77D82E7459F400B6FD13 /* OnboardingCompletedViewReactor.swift in Sources */, 3887D03A2CC5504500FB52E1 /* UITextField+Typography.swift in Sources */, 2AE6B15B2CBEAEC000FA5C3C /* ReportRequest.swift in Sources */, - 2A5BB7CE2CDBB7D100E1C799 /* ProfileImageSettingViewController.swift in Sources */, + 3889A2872E79D8090030F7CA /* AuthUseCase.swift in Sources */, + 2A5BB7CE2CDBB7D100E1C799 /* OnboardingProfileImageSettingViewController.swift in Sources */, 3893B6D22D36739500F2004C /* CompositeManager.swift in Sources */, 3800575D2D9C12CB00E58A19 /* DefinedError.swift in Sources */, + 38899E942E79518F0030F7CA /* CommonNotificationInfo.swift in Sources */, 38F131892CC7B7E0000D0475 /* RelatedTagResponse.swift in Sources */, 2AFD056E2D048CAF007C84AD /* TagRequest.swift in Sources */, 388698602D1984D600008600 /* NotificationViewReactor.swift in Sources */, + 38899E862E794CEE0030F7CA /* NetworkManager_FCM.swift in Sources */, 38C2D4212CFEB82400CEA092 /* ProfileViewReactor.swift in Sources */, 38816D9F2D004A5E00EB87D6 /* UpdateProfileViewController.swift in Sources */, 38D6F1842CC243DB00E11530 /* UITextView+Typography.swift in Sources */, @@ -2651,23 +3079,32 @@ 38CE94C02C904D460004B238 /* SOMNavigationBar.swift in Sources */, 2A5BB7D22CDC7ADC00E1C799 /* OnboardingViewController.swift in Sources */, 2AFF955B2CF3227900CBFB12 /* TagSearchTextFieldView.swift in Sources */, - 2A45B36D2CE3A3E30071026A /* ProfileImageSettingViewReactor.swift in Sources */, + 3889A26E2E79BE9F0030F7CA /* AuthRemoteDataSourceImpl.swift in Sources */, + 2A45B36D2CE3A3E30071026A /* OnboardingProfileImageSettingViewReactor.swift in Sources */, 38D5CE0C2CBCE8CA0054AB9A /* SimpleDefaults.swift in Sources */, + 383EC6232E7A56CE00EC2D1E /* AppDIContainer.swift in Sources */, 382D5CF72CFE9B8600BFA23E /* ProfileViewController.swift in Sources */, + 38899E892E794D620030F7CA /* NetworkManager_Version.swift in Sources */, 388698552D191F4B00008600 /* MainHomeDistanceViewReactor.swift in Sources */, 385620F32CA19D2D00E0AB5A /* Alamofire_Request.swift in Sources */, 388A2D312D00D6A100E2F2F0 /* FollowViewReactor.swift in Sources */, 2A649ED42CAE990B002D8284 /* SOMDialogViewController.swift in Sources */, 3893B6CF2D36728000F2004C /* ManagerProvider.swift in Sources */, + 38A7219A2E73EA6F0071E1D8 /* SelectProfileBottomFloatView.swift in Sources */, 2AFF95692CF5DFF800CBFB12 /* RecommendTagTableViewCell.swift in Sources */, 3866577F2CEF3554009F7F60 /* UIButton+Rx.swift in Sources */, 385441912C870544004E2BB0 /* AppDelegate.swift in Sources */, + 38D522692E742F610044911B /* SOMLoadingIndicatorView.swift in Sources */, 38D869642CF821F900BF87DA /* UserDefaults.swift in Sources */, + 38899E672E7939600030F7CA /* CheckAvailableResponse.swift in Sources */, 2AE6B1902CC121BB00FA5C3C /* BottomSheetSegmentTableViewCell.swift in Sources */, + 38AE77DB2E745FFF00B6FD13 /* EnterMemberTransferTextFieldView.swift in Sources */, 3878D0602CFFD45100F9522F /* FollowerResponse.swift in Sources */, + 3889A2432E79AD7D0030F7CA /* AppVersionRepository.swift in Sources */, 381701722CD88374005FC220 /* CompositeInterceptor.swift in Sources */, 2A5BB7CA2CDBA53E00E1C799 /* OnboardingNicknameSettingViewController.swift in Sources */, 385620F02CA19C9500E0AB5A /* NetworkManager.swift in Sources */, + 3889A24B2E79AE960030F7CA /* AppVersionUseCase.swift in Sources */, 2A980BA12D803EB1007DFA45 /* AnalyticsEventProtocol.swift in Sources */, 3803CF712D0159A500FD90DB /* SettingsResponse.swift in Sources */, 2A5BB7E12CDCBE7E00E1C799 /* OnboardingNicknameSettingViewReactor.swift in Sources */, @@ -2675,10 +3112,14 @@ 38F70E5F2D190FBD00B33C9D /* MainHomeTabBarReactor.swift in Sources */, 3834FADE2D11C5AC00C9108D /* SimpleCache.swift in Sources */, 388009982CAC20EC002A9209 /* SOMTags+Rx.swift in Sources */, + 38899EAA2E799C630030F7CA /* VersionRequest.swift in Sources */, 2A980BA92D803F04007DFA45 /* GAManager.swift in Sources */, 2AFF95752CF5F08700CBFB12 /* TagPreviewCardCollectionViewCell.swift in Sources */, + 38899E722E79402C0030F7CA /* ImageUrlInfo.swift in Sources */, 2AFD05472CFF75DD007C84AD /* FavoriteTagsResponse.swift in Sources */, + 3889A25C2E79BB340030F7CA /* UserRepository.swift in Sources */, 38B6AAD92CA424AE00CE6DB6 /* MoveTopButtonView.swift in Sources */, + 38899E7E2E794B420030F7CA /* SignUpResponse.swift in Sources */, 2AE6B16E2CBFBC7600FA5C3C /* UploadCardBottomSheetSegmentView.swift in Sources */, 38AA00032CAD1BCC002C5F1E /* LikeAndCommentView.swift in Sources */, 3816C0612CCDE35300C8688C /* ErrorInterceptor.swift in Sources */, @@ -2692,9 +3133,11 @@ 2AE6B1722CBFD04900FA5C3C /* SelectDefaultImageTableViewCell.swift in Sources */, 382E15432D15BA490097B09C /* NotificationWithReportViewCell.swift in Sources */, 2A45B3702CE4C5510071026A /* RegisterUserResponse.swift in Sources */, + 3889A2802E79D0250030F7CA /* Token.swift in Sources */, 388DA0FF2C8F526C00A9DD56 /* UIFont.swift in Sources */, 2AE6B1932CC1286D00FA5C3C /* SelectMyImageTableViewCell.swift in Sources */, 38F88EBF2D2C1E22002AD7A8 /* Version.swift in Sources */, + 3887176D2E7BDBAE00C6143B /* NicknameResponse.swift in Sources */, 2A980B9E2D803E9D007DFA45 /* FirebaseLoggable.swift in Sources */, 38121E2A2CA6A52400602499 /* UIRefreshControl.swift in Sources */, 3830FFA72CEC6E3100ABA9FD /* Kingfisher.swift in Sources */, @@ -2702,14 +3145,19 @@ 2A44A4382CAC227300DC463E /* BaseAuthResponse.swift in Sources */, 2AFD05562D0082DE007C84AD /* SearchTagsResponse.swift in Sources */, 385053562C92DCF900C80B02 /* SOMTabBar.swift in Sources */, + 38899E9A2E7954680030F7CA /* BlockedNotificationInfoResponse.swift in Sources */, 2AE6B14D2CBC160C00FA5C3C /* ReportViewReactor.swift in Sources */, 38FD4DAC2D032CF000BF5FF1 /* AnnouncementResponse.swift in Sources */, + 3889A2892E79D8220030F7CA /* AuthUseCaseImpl.swift in Sources */, 3880098F2CABF4C2002A9209 /* SOMTag.swift in Sources */, 3803CF892D01914200FD90DB /* ResignViewReactor.swift in Sources */, 38F70E632D19113E00B33C9D /* MainHomeLatestViewController.swift in Sources */, 388A2D342D00D7BF00E2F2F0 /* UpdateProfileViewReactor.swift in Sources */, 38B543E62D4617CB00DDF2C5 /* PushManagerConfiguration.swift in Sources */, + 3889A2462E79ADCE0030F7CA /* AppVersionRepositoryImpl.swift in Sources */, 2AE6B14A2CBC15BF00FA5C3C /* ReportViewController.swift in Sources */, + 388D8AE02E73E6190044BA79 /* SwiftEntryKit.swift in Sources */, + 38899E962E7953310030F7CA /* NotificationInfoResponse.swift in Sources */, 2AFD055A2D008D23007C84AD /* TagDetailViewController.swift in Sources */, 2AE6B1552CBCC34B00FA5C3C /* ReportReasonView.swift in Sources */, 2AFF95562CF3222400CBFB12 /* TagsViewController.swift in Sources */, @@ -2729,18 +3177,23 @@ 385441922C870544004E2BB0 /* SceneDelegate.swift in Sources */, 38D563852D1719B1006265AA /* SOMSwipeTabBarItem.swift in Sources */, 389EF8182D2F450000E053AE /* Log.swift in Sources */, + 3889A26B2E79BD450030F7CA /* AuthRemoteDataSource.swift in Sources */, 382E15372D15A6460097B09C /* NotificationTabBarController.swift in Sources */, 38F720A82CD4F15900DF32B5 /* CommentCardResponse.swift in Sources */, 38C2D41B2CFEAAED00CEA092 /* ProfileViewFooter.swift in Sources */, 389681112CAFBD6A00FFD89F /* DetailViewReactor.swift in Sources */, + 3889A2932E79D8F80030F7CA /* NotificationUseCase.swift in Sources */, 3878D0822CFFEC6900F9522F /* TermsOfServiceViewController.swift in Sources */, 38572CDC2D22464F00B07C69 /* PungTimeView.swift in Sources */, 38F720B92CD4F16500DF32B5 /* CardProtocol.swift in Sources */, + 3889A2662E79BBC40030F7CA /* UserUseCase.swift in Sources */, 38121E352CA6DA4000602499 /* Date.swift in Sources */, 2A34AFB62D144F08007BD7E7 /* EmptyTagDetailTableViewCell.swift in Sources */, + 38899E6B2E793AFD0030F7CA /* CheckAvailable.swift in Sources */, 3803CF752D0166D700FD90DB /* CommentHistoryViewCell.swift in Sources */, 38C2D4182CFEAACA00CEA092 /* ProfileViewFooterCell.swift in Sources */, 381DEA8C2CD4BBCB009F1FE9 /* WriteCardTextView+Rx.swift in Sources */, + 3889A2752E79C1D80030F7CA /* NotificationRemoteDataSource.swift in Sources */, 388698662D1998DB00008600 /* NotificationTabBarReactor.swift in Sources */, 381A1D752CC3D799005FDB8E /* SOMTagsDelegate.swift in Sources */, 2A048E7C2C9BDF5F00FFD485 /* SOMLocationFilter.swift in Sources */, @@ -2759,13 +3212,16 @@ 388009952CABFAAA002A9209 /* SOMTags.swift in Sources */, 3878F4752CA3F06C00AA46A2 /* UIStackView.swift in Sources */, 3878D0862CFFED7800F9522F /* TermsOfServiceTextCellView.swift in Sources */, - 2ACBD4142CC944FB0057C013 /* UploadRequest.swift in Sources */, + 38899E832E794C360030F7CA /* LoginResponse.swift in Sources */, 2ACBD41B2CCA03790057C013 /* ImageURLWithName.swift in Sources */, 38B6AAE02CA4777200CE6DB6 /* UIViewController+Rx.swift in Sources */, 2ACBD41E2CCAB3490057C013 /* PresignedStorageResponse.swift in Sources */, 2A5BB7DA2CDCBA8400E1C799 /* OnboardingNicknameTextFieldView.swift in Sources */, + 38899EA42E799B260030F7CA /* AppVersionRemoteDataSource.swift in Sources */, 3878D0682CFFDAF100F9522F /* OtherFollowViewCell.swift in Sources */, 38F006AB2D395A7F001AC5F7 /* SuspensionResponse.swift in Sources */, + 38899EA62E799BD60030F7CA /* AppVersionRemoteDataSourceImpl.swift in Sources */, + 38AE77D42E74580000B6FD13 /* OnboardingCompletedViewController.swift in Sources */, 2AE6B1792CBFE49D00FA5C3C /* SelectFontTableViewCell.swift in Sources */, 389EF81B2D2F454600E053AE /* Log+Extract.swift in Sources */, 381701792CD88854005FC220 /* LogginMonitor.swift in Sources */, @@ -2773,27 +3229,40 @@ 388DA1052C8F545E00A9DD56 /* Typography+SOOUM.swift in Sources */, 2AFD05612D009FA1007C84AD /* TagDetailViewrReactor.swift in Sources */, 3887D0372CC5335D00FB52E1 /* WriteCardView.swift in Sources */, + 3889A2952E79D9250030F7CA /* NotificationUseCaseImpl.swift in Sources */, 38B8A58C2CAEA79A000AFE83 /* DetailViewFooter.swift in Sources */, + 3889A2512E79B3260030F7CA /* UserRemoteDataSource.swift in Sources */, + 38899E5E2E7937E50030F7CA /* NicknameValidateResponse.swift in Sources */, 385053532C92DBE200C80B02 /* SOMTabBarItem.swift in Sources */, 3803CF6D2D0156FC00FD90DB /* SettingsRequest.swift in Sources */, + 3889A28F2E79D8860030F7CA /* NotificationRepositoryImpl.swift in Sources */, 2A5BB7E42CDCD97300E1C799 /* JoinRequest.swift in Sources */, + 38B65E7A2E72A29F00DF6919 /* OnboardingNumberingView.swift in Sources */, 2AFD055E2D009513007C84AD /* TagDetailNavigationBarView.swift in Sources */, + 3889A28C2E79D86B0030F7CA /* NotificationRepository.swift in Sources */, 2AFF95652CF33D9F00CBFB12 /* TagsHeaderView.swift in Sources */, 38B543EC2D461B1A00DDF2C5 /* LocationManagerConfigruation.swift in Sources */, 38A5D1552C8CB12300B68363 /* UIImage+SOOUM.swift in Sources */, 385602B72D2FB18400118530 /* NotiPlaceholderViewCell.swift in Sources */, + 383EC6152E7A50EB00EC2D1E /* AuthLocalDataSourceImpl.swift in Sources */, 3878D0542CFFC6C100F9522F /* ProfileResponse.swift in Sources */, + 3889A27D2E79C56E0030F7CA /* ToeknResponse.swift in Sources */, 3878D06C2CFFDF1F00F9522F /* SettingsViewController.swift in Sources */, 38FD4DAF2D032FCE00BF5FF1 /* AnnouncementViewReactor.swift in Sources */, 3802BDAD2D0AC1FB001256EA /* UIImage.swift in Sources */, + 3889A2622E79BB5B0030F7CA /* UserRepositoryImpl.swift in Sources */, 38F70E662D19161800B33C9D /* MainHomeLatestViewReactor.swift in Sources */, + 3889A2842E79D7D40030F7CA /* AuthRepositoryImpl.swift in Sources */, + 383EC6202E7A564600EC2D1E /* AppAssembler.swift in Sources */, 381DEA8D2CD4BE4A009F1FE9 /* Card.swift in Sources */, 384972A42CA54DC10012FCA1 /* UIImgeView.swift in Sources */, 38D6F1812CC2413400E11530 /* WriteCardTextView.swift in Sources */, 3816E2382D3BEE7E004CC196 /* TermsOfServiceCellView.swift in Sources */, + 38AE77DE2E7465F500B6FD13 /* EnterMemberTransferTextFieldView+Rx.swift in Sources */, 3878F4722CA3F03400AA46A2 /* SOMCard.swift in Sources */, 3803CF862D017DC700FD90DB /* EnterMemberTransferViewReactor.swift in Sources */, 38B6AAE32CA4787200CE6DB6 /* MainTabBarReactor.swift in Sources */, + 38899E592E7936DD0030F7CA /* SooumStyle_V2.swift in Sources */, 38B8A5852CAE9CC4000AFE83 /* MainHomeViewCell.swift in Sources */, 3878D0642CFFD66700F9522F /* FollowViewController.swift in Sources */, 38026E402CA2B45A0045E1CE /* LocationManager.swift in Sources */, @@ -2825,7 +3294,6 @@ buildActionMask = 2147483647; files = ( 38FD4DAE2D032FCE00BF5FF1 /* AnnouncementViewReactor.swift in Sources */, - 2A44A4342CAC21A500DC463E /* SignUpResponse.swift in Sources */, 385620F62CA19EA900E0AB5A /* Alamofire_constants.swift in Sources */, 38F88EBA2D2C1CB8002AD7A8 /* Info.swift in Sources */, 3834FADD2D11C5AC00C9108D /* SimpleCache.swift in Sources */, @@ -2836,16 +3304,19 @@ 2A048E842C9BE01300FFD485 /* SOMLocationFilterCollectionViewCell.swift in Sources */, 38AE565C2D048B4800CAA431 /* SOMDialogViewController+Show.swift in Sources */, 385E65A32CBE56D00032E120 /* Coordinate.swift in Sources */, + 3889A2692E79BC880030F7CA /* UserUseCaseImpl.swift in Sources */, 3878FE0D2D0365C800D8955C /* SOMNavigationBar+Rx.swift in Sources */, 2AE6B1632CBFB7FB00FA5C3C /* UploadCardBottomSheetViewController.swift in Sources */, 38FD4DB42D034F6600BF5FF1 /* MyFollowerViewCell.swift in Sources */, 3878D04E2CFFC5F300F9522F /* ProfileRequest.swift in Sources */, + 383EC6192E7A547900EC2D1E /* BaseAssembler.swift in Sources */, 3878D0722CFFDFEF00F9522F /* SettingTextCellView+Rx.swift in Sources */, 38121E292CA6A52400602499 /* UIRefreshControl.swift in Sources */, 388371F92C8C8EB1004212EB /* SooumStyle.swift in Sources */, 38389B9C2CCCF98B006728AF /* AuthRequest.swift in Sources */, 2A5ABA352D464E0B00BF6C9B /* ConfigureRequest.swift in Sources */, 38D6F17C2CC2406700E11530 /* WriteCardViewController.swift in Sources */, + 38899E8F2E7951200030F7CA /* KeyInfoResponse.swift in Sources */, 3878F4772CA3F08300AA46A2 /* UIView.swift in Sources */, 388698512D191F2100008600 /* MainHomeDistanceViewController.swift in Sources */, 3830FFA62CEC6E3100ABA9FD /* Kingfisher.swift in Sources */, @@ -2856,6 +3327,8 @@ 3803CF822D017DB800FD90DB /* EnterMemberTransferViewController.swift in Sources */, 2AE6B14C2CBC160C00FA5C3C /* ReportViewReactor.swift in Sources */, 388009912CABF855002A9209 /* SOMTagModel.swift in Sources */, + 3889A2562E79BA160030F7CA /* UserRemoteDataSourceImpl.swift in Sources */, + 3889A24E2E79AEB30030F7CA /* AppVersionUseCaseImpl.swift in Sources */, 38FD4DAB2D032CF000BF5FF1 /* AnnouncementResponse.swift in Sources */, 388FCAD02CFAC2BF0012C4D6 /* Notification.swift in Sources */, 38608B302CB5195D0066BB40 /* Card.swift in Sources */, @@ -2871,26 +3344,33 @@ 38F70E5B2D1905D000B33C9D /* MainHomeTabBarController.swift in Sources */, 385009C22D363525007175A1 /* FilterNil.swift in Sources */, 38B6AADB2CA4740B00CE6DB6 /* LaunchScreenViewReactor.swift in Sources */, + 383EC61C2E7A548E00EC2D1E /* BaseDIContainer.swift in Sources */, 2A5BB7D12CDC7ADC00E1C799 /* OnboardingViewController.swift in Sources */, 3887D0392CC5504500FB52E1 /* UITextField+Typography.swift in Sources */, 3803CF7A2D016BDB00FD90DB /* IssueMemberTransferViewReactor.swift in Sources */, 38C2D4172CFEAACA00CEA092 /* ProfileViewFooterCell.swift in Sources */, 38E7FBEF2D3CF6BB00A359CD /* SOMDialogAction.swift in Sources */, - 38AA66262D3AC3F500B3F6B2 /* DialogMessageView.swift in Sources */, 38AA00022CAD1BCC002C5F1E /* LikeAndCommentView.swift in Sources */, 2AE6B1542CBCC34B00FA5C3C /* ReportReasonView.swift in Sources */, 38D5CE0B2CBCE8CA0054AB9A /* SimpleDefaults.swift in Sources */, + 38899E752E79430A0030F7CA /* SignUpRequestInfo.swift in Sources */, 38E9CE102D376E0E00E85A2D /* PushTokenSet.swift in Sources */, 2AFD05602D009FA1007C84AD /* TagDetailViewrReactor.swift in Sources */, 385053582C92DD2300C80B02 /* SOMTabBarController.swift in Sources */, + 38899E6E2E79400C0030F7CA /* ImageUrlInfoResponse.swift in Sources */, 381A1D6A2CC398B3005FDB8E /* WriteTagTextFieldDelegate.swift in Sources */, + 3889A2782E79C29F0030F7CA /* NotificationRemoteDataSoruceImpl.swift in Sources */, 38F70E622D19113E00B33C9D /* MainHomeLatestViewController.swift in Sources */, 38F70E6F2D191DFB00B33C9D /* MainHomePopularViewReactor.swift in Sources */, + 38899E9D2E7954D90030F7CA /* DeleteNotificationInfoResponse.swift in Sources */, 3878D0792CFFE1E800F9522F /* ResignViewController.swift in Sources */, + 38899E8D2E794E690030F7CA /* AppVersionStatusResponse.swift in Sources */, + 38A721952E73E7140071E1D8 /* View+SwiftEntryKit.swift in Sources */, 38F720B12CD4F15900DF32B5 /* distanceCardResponse.swift in Sources */, 38816DA22D004DED00EB87D6 /* UpdateProfileView.swift in Sources */, 38FDC2B62C9E746B00C094C2 /* BaseViewController.swift in Sources */, 2A980BA42D803EEA007DFA45 /* SOMEvent.swift in Sources */, + 38B65E7D2E72ADB900DF6919 /* TermsOfServiceAgreeButtonView+Rx.swift in Sources */, 2AFD05492CFF7687007C84AD /* RecommendTagsResponse.swift in Sources */, 2AE6B17F2CBFEA5200FA5C3C /* ToggleView.swift in Sources */, 38601E1B2D3139D000A465A9 /* RecommendTagView.swift in Sources */, @@ -2902,6 +3382,7 @@ 382D5CF62CFE9B8600BFA23E /* ProfileViewController.swift in Sources */, 38773E7C2CB3ACB2004815CD /* SOMRefreshControl.swift in Sources */, 3817016E2CD882C2005FC220 /* TimeoutInterceptor.swift in Sources */, + 383EC6112E7A4F6B00EC2D1E /* AuthLocalDataSource.swift in Sources */, 388A2D302D00D6A100E2F2F0 /* FollowViewReactor.swift in Sources */, 389EF81E2D2F469B00E053AE /* CocoaLumberjack.swift in Sources */, 3880098E2CABF4C2002A9209 /* SOMTag.swift in Sources */, @@ -2911,16 +3392,22 @@ 38CC49822CDE3854007A0145 /* SOMPresentationController.swift in Sources */, 3802BDAC2D0AC1FB001256EA /* UIImage.swift in Sources */, 389EF81A2D2F454600E053AE /* Log+Extract.swift in Sources */, + 38899EAE2E79A09B0030F7CA /* UserRequest.swift in Sources */, 38C2D4142CFEA9CC00CEA092 /* MyProfileViewCell.swift in Sources */, + 3889A2712E79C03B0030F7CA /* AuthRepository.swift in Sources */, 3803CF882D01914200FD90DB /* ResignViewReactor.swift in Sources */, + 38AE77D72E7459F400B6FD13 /* OnboardingCompletedViewReactor.swift in Sources */, 38816D9E2D004A5E00EB87D6 /* UpdateProfileViewController.swift in Sources */, 38D8E2912CCD232B00CE2E0A /* AuthManager.swift in Sources */, + 3889A2862E79D8090030F7CA /* AuthUseCase.swift in Sources */, 3886985F2D1984D600008600 /* NotificationViewReactor.swift in Sources */, 2AFD05692D03264C007C84AD /* AddFavoriteTagResponse.swift in Sources */, 3800575C2D9C12CB00E58A19 /* DefinedError.swift in Sources */, + 38899E932E79518F0030F7CA /* CommonNotificationInfo.swift in Sources */, 3893B6D12D36739500F2004C /* CompositeManager.swift in Sources */, 3836ACB72C8F04CD00A3C566 /* UILabel+Observer.swift in Sources */, 3816C0602CCDE35300C8688C /* ErrorInterceptor.swift in Sources */, + 38899E872E794CEE0030F7CA /* NetworkManager_FCM.swift in Sources */, 3803CF7D2D016DA200FD90DB /* TransferCodeResponse.swift in Sources */, 2AFF955A2CF3227900CBFB12 /* TagSearchTextFieldView.swift in Sources */, 2A44A42D2CAC14C800DC463E /* SignInResponse.swift in Sources */, @@ -2940,23 +3427,32 @@ 388371FC2C8C8F11004212EB /* UIColor+SOOUM.swift in Sources */, 381A1D742CC3D799005FDB8E /* SOMTagsDelegate.swift in Sources */, 2AE6B1782CBFE49D00FA5C3C /* SelectFontTableViewCell.swift in Sources */, + 3889A26F2E79BE9F0030F7CA /* AuthRemoteDataSourceImpl.swift in Sources */, 3850719A2CA295A800A7905A /* LaunchScreenViewController.swift in Sources */, 38D055C32CD862FE00E75590 /* SOMActivityIndicatorView.swift in Sources */, + 383EC6242E7A56CE00EC2D1E /* AppDIContainer.swift in Sources */, 38CE94BF2C904D460004B238 /* SOMNavigationBar.swift in Sources */, + 38899E8A2E794D620030F7CA /* NetworkManager_Version.swift in Sources */, 2A45B36F2CE4C5510071026A /* RegisterUserResponse.swift in Sources */, 388698582D1982DE00008600 /* NotificationViewController.swift in Sources */, 38121E342CA6DA4000602499 /* Date.swift in Sources */, 38F720A52CD4F15900DF32B5 /* CardSummaryResponse.swift in Sources */, 3878D0752CFFE01500F9522F /* SettingScrollViewHeader.swift in Sources */, + 38A721992E73EA6F0071E1D8 /* SelectProfileBottomFloatView.swift in Sources */, 3893B6CE2D36728000F2004C /* ManagerProvider.swift in Sources */, 2AFF95682CF5DFF800CBFB12 /* RecommendTagTableViewCell.swift in Sources */, 3880097B2CABEE3D002A9209 /* DetailViewController.swift in Sources */, + 38D522682E742F610044911B /* SOMLoadingIndicatorView.swift in Sources */, 38D869632CF821F900BF87DA /* UserDefaults.swift in Sources */, + 38899E662E7939600030F7CA /* CheckAvailableResponse.swift in Sources */, 385620F22CA19D2D00E0AB5A /* Alamofire_Request.swift in Sources */, + 38AE77DC2E745FFF00B6FD13 /* EnterMemberTransferTextFieldView.swift in Sources */, 38389B9F2CCCFB7D006728AF /* AuthKeyChain.swift in Sources */, + 3889A2442E79AD7D0030F7CA /* AppVersionRepository.swift in Sources */, 3878B8622D0DC8BD00B3B128 /* UIViewController+Toast.swift in Sources */, 2AFD05522D007F2F007C84AD /* TagSearchViewReactor.swift in Sources */, 387FBAF02C8702C100A5E139 /* AppDelegate.swift in Sources */, + 3889A24A2E79AE960030F7CA /* AppVersionUseCase.swift in Sources */, 2A980BA02D803EB1007DFA45 /* AnalyticsEventProtocol.swift in Sources */, 38B8A58E2CAEB61A000AFE83 /* DetailViewFooterCell.swift in Sources */, 2A5BB7C92CDBA53E00E1C799 /* OnboardingNicknameSettingViewController.swift in Sources */, @@ -2964,10 +3460,14 @@ 2AE6B1492CBC15BF00FA5C3C /* ReportViewController.swift in Sources */, 3887D0332CC5335200FB52E1 /* WriteCardViewReactor.swift in Sources */, 38D6F1802CC2413400E11530 /* WriteCardTextView.swift in Sources */, + 38899EA92E799C630030F7CA /* VersionRequest.swift in Sources */, 2A980BA82D803F04007DFA45 /* GAManager.swift in Sources */, 387D852C2D08320A005D9D22 /* SOMCardModel.swift in Sources */, + 38899E712E79402C0030F7CA /* ImageUrlInfo.swift in Sources */, 2AFD055D2D009513007C84AD /* TagDetailNavigationBarView.swift in Sources */, + 3889A25D2E79BB340030F7CA /* UserRepository.swift in Sources */, 385620EF2CA19C9500E0AB5A /* NetworkManager.swift in Sources */, + 38899E7D2E794B420030F7CA /* SignUpResponse.swift in Sources */, 3803CF692D0156BA00FD90DB /* SettingsViewReactor.swift in Sources */, 38F720B82CD4F16500DF32B5 /* CardProtocol.swift in Sources */, 38F131882CC7B7E0000D0475 /* RelatedTagResponse.swift in Sources */, @@ -2981,9 +3481,11 @@ 2AE6B1712CBFD04900FA5C3C /* SelectDefaultImageTableViewCell.swift in Sources */, 2AE6B1662CBFB81000FA5C3C /* UploadCardBottomSheetViewReactor.swift in Sources */, 38FD4DB12D034C1700BF5FF1 /* MyFollowingViewCell.swift in Sources */, + 3889A2812E79D0250030F7CA /* Token.swift in Sources */, 2ACBD41D2CCAB3490057C013 /* PresignedStorageResponse.swift in Sources */, 2ACBD4172CC963390057C013 /* DefaultCardImageResponse.swift in Sources */, 38AA00062CAD96E3002C5F1E /* MoreBottomSheetViewController.swift in Sources */, + 3887176E2E7BDBAE00C6143B /* NicknameResponse.swift in Sources */, 2A980B9D2D803E9D007DFA45 /* FirebaseLoggable.swift in Sources */, 382E153A2D15A67A0097B09C /* NotificationViewCell.swift in Sources */, 3886939F2CF77FA7005F9EF3 /* UIApplication+Top.swift in Sources */, @@ -2991,14 +3493,18 @@ 3878D0972CFFF2B800F9522F /* CommentHistroyViewController.swift in Sources */, 2A44A42A2CAC09AE00DC463E /* RSAKeyResponse.swift in Sources */, 2A5BB7B92CDB860D00E1C799 /* OnboardingTermsOfServiceViewController.swift in Sources */, + 38899E992E7954680030F7CA /* BlockedNotificationInfoResponse.swift in Sources */, 2AFD05462CFF75DD007C84AD /* FavoriteTagsResponse.swift in Sources */, 2AFD054F2CFF79D8007C84AD /* TagsViewReactor.swift in Sources */, - 2ACBD4132CC944FB0057C013 /* UploadRequest.swift in Sources */, + 3889A28A2E79D8220030F7CA /* AuthUseCaseImpl.swift in Sources */, 388698652D1998DB00008600 /* NotificationTabBarReactor.swift in Sources */, 388DA0FE2C8F526C00A9DD56 /* UIFont.swift in Sources */, 38F720AD2CD4F15900DF32B5 /* DetailCardResponse.swift in Sources */, 38B543E52D4617CB00DDF2C5 /* PushManagerConfiguration.swift in Sources */, 2AE6B1502CBCC2F600FA5C3C /* ReportTableViewCell.swift in Sources */, + 3889A2472E79ADCE0030F7CA /* AppVersionRepositoryImpl.swift in Sources */, + 388D8ADF2E73E6190044BA79 /* SwiftEntryKit.swift in Sources */, + 38899E972E7953310030F7CA /* NotificationInfoResponse.swift in Sources */, 2AFF95552CF3222400CBFB12 /* TagsViewController.swift in Sources */, 2AE6B1922CC1286D00FA5C3C /* SelectMyImageTableViewCell.swift in Sources */, 2AE6B18F2CC121BB00FA5C3C /* BottomSheetSegmentTableViewCell.swift in Sources */, @@ -3014,23 +3520,28 @@ 388A2D332D00D7BF00E2F2F0 /* UpdateProfileViewReactor.swift in Sources */, 38C2D41A2CFEAAED00CEA092 /* ProfileViewFooter.swift in Sources */, 3887D0362CC5335D00FB52E1 /* WriteCardView.swift in Sources */, - 2A5BB7CD2CDBB7D100E1C799 /* ProfileImageSettingViewController.swift in Sources */, + 2A5BB7CD2CDBB7D100E1C799 /* OnboardingProfileImageSettingViewController.swift in Sources */, 385053552C92DCF900C80B02 /* SOMTabBar.swift in Sources */, 387FBAF22C8702C100A5E139 /* SceneDelegate.swift in Sources */, 2A048E7B2C9BDF5F00FFD485 /* SOMLocationFilter.swift in Sources */, 38121E312CA6C77500602499 /* Double.swift in Sources */, + 3889A26C2E79BD450030F7CA /* AuthRemoteDataSource.swift in Sources */, 3878D0532CFFC6C100F9522F /* ProfileResponse.swift in Sources */, 2AE6B1752CBFD59B00FA5C3C /* ImageCollectionViewCell.swift in Sources */, 38601E1A2D3139BB00A465A9 /* TagInfoResponse.swift in Sources */, 382E15362D15A6460097B09C /* NotificationTabBarController.swift in Sources */, + 3889A2922E79D8F80030F7CA /* NotificationUseCase.swift in Sources */, 2A34AFB52D144F08007BD7E7 /* EmptyTagDetailTableViewCell.swift in Sources */, 3803CF742D0166D700FD90DB /* CommentHistoryViewCell.swift in Sources */, 2AFF95702CF5E8DE00CBFB12 /* TagSearchViewController.swift in Sources */, + 3889A2652E79BBC40030F7CA /* UserUseCase.swift in Sources */, 384972A32CA54DC10012FCA1 /* UIImgeView.swift in Sources */, + 38899E6C2E793AFD0030F7CA /* CheckAvailable.swift in Sources */, 3802BDB12D0AE900001256EA /* PushManager.swift in Sources */, 2A5BB7E72CDCDC3600E1C799 /* NicknameValidationResponse.swift in Sources */, 388372012C8C8FCF004212EB /* UIColor.swift in Sources */, 3803CF772D01685000FD90DB /* CommentHistroyViewReactor.swift in Sources */, + 3889A2742E79C1D80030F7CA /* NotificationRemoteDataSource.swift in Sources */, 3816C05C2CCDDF3D00C8688C /* ReAuthenticationResponse.swift in Sources */, 3878D07D2CFFE6E500F9522F /* IssueMemberTransferViewController.swift in Sources */, 381DEA8B2CD4BBCB009F1FE9 /* WriteCardTextView+Rx.swift in Sources */, @@ -3049,12 +3560,16 @@ 388DA1042C8F545E00A9DD56 /* Typography+SOOUM.swift in Sources */, 38C2D4112CFE9EF300CEA092 /* OtherProfileViewCell.swift in Sources */, 2AFF955D2CF328DE00CBFB12 /* FavoriteTagTableViewCell.swift in Sources */, + 38899E842E794C360030F7CA /* LoginResponse.swift in Sources */, 385053522C92DBE200C80B02 /* SOMTabBarItem.swift in Sources */, 38A5D1542C8CB11E00B68363 /* UIImage+SOOUM.swift in Sources */, 38F70E5E2D190FBD00B33C9D /* MainHomeTabBarReactor.swift in Sources */, 38601E182D31399400A465A9 /* CardRequest.swift in Sources */, - 2A5BB7FA2CE277AF00E1C799 /* ProfileImageSettingViewReactor.swift in Sources */, + 38899EA32E799B260030F7CA /* AppVersionRemoteDataSource.swift in Sources */, + 2A5BB7FA2CE277AF00E1C799 /* OnboardingProfileImageSettingViewReactor.swift in Sources */, 38F006AA2D395A7F001AC5F7 /* SuspensionResponse.swift in Sources */, + 38899EA72E799BD60030F7CA /* AppVersionRemoteDataSourceImpl.swift in Sources */, + 38AE77D52E74580000B6FD13 /* OnboardingCompletedViewController.swift in Sources */, 3878D06F2CFFDF9600F9522F /* SettingTextCellView.swift in Sources */, 38B8A5842CAE9CC4000AFE83 /* MainHomeViewCell.swift in Sources */, 2AFF95612CF33A3900CBFB12 /* FavoriteTagView.swift in Sources */, @@ -3062,27 +3577,40 @@ 38D488CA2D0C557300F2D38D /* SOMButton.swift in Sources */, 3878F4712CA3F03400AA46A2 /* SOMCard.swift in Sources */, 3878D06B2CFFDF1F00F9522F /* SettingsViewController.swift in Sources */, + 3889A2962E79D9250030F7CA /* NotificationUseCaseImpl.swift in Sources */, 3878D0882CFFEF0F00F9522F /* TermsOfServiceTextCellView+Rx.swift in Sources */, + 3889A2502E79B3260030F7CA /* UserRemoteDataSource.swift in Sources */, + 38899E5F2E7937E50030F7CA /* NicknameValidateResponse.swift in Sources */, 2AE6B16D2CBFBC7600FA5C3C /* UploadCardBottomSheetSegmentView.swift in Sources */, 38F720A72CD4F15900DF32B5 /* CommentCardResponse.swift in Sources */, + 3889A2902E79D8860030F7CA /* NotificationRepositoryImpl.swift in Sources */, 38B6AAE22CA4787200CE6DB6 /* MainTabBarReactor.swift in Sources */, + 38B65E792E72A29F00DF6919 /* OnboardingNumberingView.swift in Sources */, 2A5BB7D52CDCA5C900E1C799 /* TermsOfServiceAgreeButtonView.swift in Sources */, + 3889A28D2E79D86B0030F7CA /* NotificationRepository.swift in Sources */, 2AFF95782CF5F0B000CBFB12 /* TagPreviewCardView.swift in Sources */, 38B543EB2D461B1A00DDF2C5 /* LocationManagerConfigruation.swift in Sources */, 38026E3F2CA2B45A0045E1CE /* LocationManager.swift in Sources */, 388A2D2D2D00A45800E2F2F0 /* writtenCardResponse.swift in Sources */, + 383EC6162E7A50EB00EC2D1E /* AuthLocalDataSourceImpl.swift in Sources */, 3878D0852CFFED7800F9522F /* TermsOfServiceTextCellView.swift in Sources */, + 3889A27E2E79C56E0030F7CA /* ToeknResponse.swift in Sources */, 3878D0812CFFEC6900F9522F /* TermsOfServiceViewController.swift in Sources */, 38F720B32CD4F15900DF32B5 /* LatestCardResponse.swift in Sources */, 2A032EFD2CE517DD008326C0 /* OnboardingTermsOfServiceViewReactor.swift in Sources */, + 3889A2632E79BB5B0030F7CA /* UserRepositoryImpl.swift in Sources */, 38A627172CECC5A800C37A03 /* SOMTagsLayoutConfigure.swift in Sources */, + 3889A2832E79D7D40030F7CA /* AuthRepositoryImpl.swift in Sources */, + 383EC6212E7A564600EC2D1E /* AppAssembler.swift in Sources */, 2AFD05552D0082DE007C84AD /* SearchTagsResponse.swift in Sources */, 38B8A5882CAEA5F9000AFE83 /* DetailViewCell.swift in Sources */, 389EF8172D2F450000E053AE /* Log.swift in Sources */, 3816E2372D3BEE7E004CC196 /* TermsOfServiceCellView.swift in Sources */, + 38AE77DF2E7465F500B6FD13 /* EnterMemberTransferTextFieldView+Rx.swift in Sources */, 38738D4B2D2FDCC300C37574 /* WithoutReadNotisCountResponse.swift in Sources */, 388698622D1986B100008600 /* NotificationRequest.swift in Sources */, 38572CDE2D2254E800B07C69 /* PlaceholderViewCell.swift in Sources */, + 38899E582E7936DD0030F7CA /* SooumStyle_V2.swift in Sources */, 388009972CAC20EC002A9209 /* SOMTags+Rx.swift in Sources */, 388698542D191F4B00008600 /* MainHomeDistanceViewReactor.swift in Sources */, 38601E192D3139A500A465A9 /* TagDetailViewController.swift in Sources */, @@ -3121,11 +3649,9 @@ ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; CODE_SIGN_ENTITLEMENTS = "SOOUM/Resources/SOOUM-Dev.entitlements"; CODE_SIGN_IDENTITY = "Apple Development"; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; - CODE_SIGN_STYLE = Manual; + CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1012010; - DEVELOPMENT_TEAM = ""; - "DEVELOPMENT_TEAM[sdk=iphoneos*]" = 99FRG743RX; + DEVELOPMENT_TEAM = 99FRG743RX; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = "SOOUM/Resources/Develop/Info-dev.plist"; INFOPLIST_KEY_CFBundleDisplayName = "[D]SOOUM"; @@ -3152,12 +3678,11 @@ PRODUCT_BUNDLE_IDENTIFIER = com.sooum.dev; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; - "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "Profile-Dev"; SOOUM_APP_ID = 99FRG743RX.com.sooum.dev; SOOUM_CLARITY_ID = qrggvyniav; SOOUM_DISPLAY_NAME = "[D]SOOUM"; SOOUM_LOCATION_DESCRIPTION = "사용자의 위치 기반으로 피드 정보를 제공하기 위해 권한이 필요합니다."; - SOOUM_SERVER_ENDPOINT = "ec2-52-79-234-222.ap-northeast-2.compute.amazonaws.com:8080"; + SOOUM_SERVER_ENDPOINT = "test-core.sooum.org:555"; SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; SUPPORTS_MACCATALYST = NO; SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; @@ -3175,11 +3700,9 @@ ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; CODE_SIGN_ENTITLEMENTS = "SOOUM/Resources/SOOUM-Dev.entitlements"; CODE_SIGN_IDENTITY = "Apple Development"; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; - CODE_SIGN_STYLE = Manual; + CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1012010; - DEVELOPMENT_TEAM = ""; - "DEVELOPMENT_TEAM[sdk=iphoneos*]" = 99FRG743RX; + DEVELOPMENT_TEAM = 99FRG743RX; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = "SOOUM/Resources/Develop/Info-dev.plist"; INFOPLIST_KEY_CFBundleDisplayName = "[D]SOOUM"; @@ -3206,12 +3729,11 @@ PRODUCT_BUNDLE_IDENTIFIER = com.sooum.dev; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; - "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "Profile-Dev"; SOOUM_APP_ID = 99FRG743RX.com.sooum.dev; SOOUM_CLARITY_ID = qrggvyniav; SOOUM_DISPLAY_NAME = "[D]SOOUM"; SOOUM_LOCATION_DESCRIPTION = "사용자의 위치 기반으로 피드 정보를 제공하기 위해 권한이 필요합니다."; - SOOUM_SERVER_ENDPOINT = "ec2-52-79-234-222.ap-northeast-2.compute.amazonaws.com:8080"; + SOOUM_SERVER_ENDPOINT = "test-core.sooum.org:555"; SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; SUPPORTS_MACCATALYST = NO; SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; diff --git a/SOOUM/SOOUM.xcodeproj/xcshareddata/xcschemes/SOOUM-Dev.xcscheme b/SOOUM/SOOUM.xcodeproj/xcshareddata/xcschemes/SOOUM-Dev.xcscheme index 98c404de..79ef35ab 100644 --- a/SOOUM/SOOUM.xcodeproj/xcshareddata/xcschemes/SOOUM-Dev.xcscheme +++ b/SOOUM/SOOUM.xcodeproj/xcshareddata/xcschemes/SOOUM-Dev.xcscheme @@ -66,6 +66,10 @@ + + diff --git a/SOOUM/SOOUM/App/AppDelegate.swift b/SOOUM/SOOUM/App/AppDelegate.swift index e3c7d775..8c14591f 100644 --- a/SOOUM/SOOUM/App/AppDelegate.swift +++ b/SOOUM/SOOUM/App/AppDelegate.swift @@ -21,7 +21,7 @@ import CocoaLumberjack @main class AppDelegate: UIResponder, UIApplicationDelegate { - let provider: ManagerProviderType = ManagerProviderContainer() + let appDIContainer: AppDIContainerable = AppDIContainer() /// APNS 등록 완료 핸들러 var registerRemoteNotificationCompletion: ((Error?) -> Void)? @@ -45,9 +45,6 @@ class AppDelegate: UIResponder, UIApplicationDelegate { // Initalize token self.initializeTokenWhenFirstLaunch() - // Set managers - self.provider.initialize() - FirebaseApp.configure() // 파이어베이스 Meesaging 설정 Messaging.messaging().delegate = self @@ -108,13 +105,13 @@ extension AppDelegate: UNUserNotificationCenterDelegate { didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void ) { - let userInfo: [AnyHashable: Any] = response.notification.request.content.userInfo - if let infoDic = userInfo as? [String: Any] { - - let info = NotificationInfo(infoDic) - // 계정 이관 성공 알림일 경우 (런치 화면 > 온보딩 화면), 아닐 경우 메인 홈 탭바 화면 전환 - self.provider.pushManager.setupRootViewController(info, terminated: info.isTransfered) - } + // let userInfo: [AnyHashable: Any] = response.notification.request.content.userInfo + // if let infoDic = userInfo as? [String: Any] { + // + // let info = NotificationInfo(infoDic) + // // 계정 이관 성공 알림일 경우 (런치 화면 > 온보딩 화면), 아닐 경우 메인 홈 탭바 화면 전환 + // self.provider.pushManager.setupRootViewController(info, terminated: info.isTransfered) + // } completionHandler() } @@ -154,7 +151,8 @@ extension AppDelegate: MessagingDelegate { apns: deviceToken, fcm: Messaging.messaging().fcmToken ) - self.provider.networkManager.registerFCMToken(with: current, #function) + let provider = self.appDIContainer.rootContainer.resolve(ManagerProviderType.self) + provider.networkManager.registerFCMToken(with: current, #function) self.registerRemoteNotificationCompletion?(nil) } @@ -165,7 +163,8 @@ extension AppDelegate: MessagingDelegate { apns: messaging.apnsToken, fcm: fcmToken ) - self.provider.networkManager.registerFCMToken(with: current, #function) + let provider = self.appDIContainer.rootContainer.resolve(ManagerProviderType.self) + provider.networkManager.registerFCMToken(with: current, #function) } } @@ -190,12 +189,12 @@ extension AppDelegate { } private func setupOnboardingWhenTransferSuccessed(_ userInfo: [AnyHashable: Any]?) { - guard let infoDic = userInfo as? [String: Any] else { return } + // guard let infoDic = userInfo as? [String: Any] else { return } - let info = NotificationInfo(infoDic) - if info.isTransfered { - - self.provider.pushManager.setupRootViewController(info, terminated: true) - } + // let info = NotificationInfo(infoDic) + // if info.isTransfered { + // + // self.provider.pushManager.setupRootViewController(info, terminated: true) + // } } } diff --git a/SOOUM/SOOUM/App/SceneDelegate.swift b/SOOUM/SOOUM/App/SceneDelegate.swift index 2873f331..1f2637ab 100644 --- a/SOOUM/SOOUM/App/SceneDelegate.swift +++ b/SOOUM/SOOUM/App/SceneDelegate.swift @@ -23,25 +23,25 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate { guard let windowScene = (scene as? UIWindowScene) else { return } guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else { return } - window = UIWindow(frame: windowScene.coordinateSpace.bounds) - window?.windowScene = windowScene + self.window = UIWindow(frame: windowScene.coordinateSpace.bounds) + self.window?.windowScene = windowScene let viewController = LaunchScreenViewController() - viewController.reactor = LaunchScreenViewReactor(provider: appDelegate.provider) + viewController.reactor = LaunchScreenViewReactor(dependencies: appDelegate.appDIContainer) - window?.rootViewController = viewController - window?.backgroundColor = .white - window?.makeKeyAndVisible() + self.window?.rootViewController = viewController + self.window?.backgroundColor = .white + self.window?.makeKeyAndVisible() /// 앱이 완전히 종료되었을 때 push notification에 대한 응답을 했을 때 실행할 코드 작성 if let response: UNNotificationResponse = connectionOptions.notificationResponse { - let userInfo: [AnyHashable: Any] = response.notification.request.content.userInfo + // let userInfo: [AnyHashable: Any] = response.notification.request.content.userInfo - if let infoDic: [String: Any] = userInfo as? [String: Any] { - - let info = NotificationInfo(infoDic) - appDelegate.provider.pushManager.setupRootViewController(info, terminated: true) - } + // if let infoDic: [String: Any] = userInfo as? [String: Any] { + // + // let info = NotificationInfo(infoDic) + // appDelegate.provider.pushManager.setupRootViewController(info, terminated: true) + // } } } diff --git a/SOOUM/SOOUM/Base/BaseNavigationViewController.swift b/SOOUM/SOOUM/Base/BaseNavigationViewController.swift index 3219bc2d..9c628c5d 100644 --- a/SOOUM/SOOUM/Base/BaseNavigationViewController.swift +++ b/SOOUM/SOOUM/Base/BaseNavigationViewController.swift @@ -101,14 +101,13 @@ class BaseNavigationViewController: BaseViewController { $0.trailing.equalTo(self.navigationBar.snp.trailing) $0.height.equalTo(1.4) } + // 로딩 뷰는 항상 최상단에 표시 + self.view.bringSubviewToFront(self.loadingIndicatorView) } override func bind() { super.bind() - self.navigationController?.delegate = self - self.navigationController?.interactivePopGestureRecognizer?.delegate = self - self.backButton.rx.tap .subscribe(with: self) { object, _ in object.navigationPop( @@ -147,7 +146,14 @@ class BaseNavigationViewController: BaseViewController { extension BaseNavigationViewController: UIGestureRecognizerDelegate { - public func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool { + func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool { + return self.navigationPopGestureEnabled + } + + func gestureRecognizer( + _ gestureRecognizer: UIGestureRecognizer, + shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer + ) -> Bool { return self.navigationPopGestureEnabled } } diff --git a/SOOUM/SOOUM/Base/BaseViewController.swift b/SOOUM/SOOUM/Base/BaseViewController.swift index aa8ceb40..650b8af7 100644 --- a/SOOUM/SOOUM/Base/BaseViewController.swift +++ b/SOOUM/SOOUM/Base/BaseViewController.swift @@ -13,12 +13,15 @@ import RxSwift import SnapKit import Then +import Lottie + class BaseViewController: UIViewController { var disposeBag = DisposeBag() let activityIndicatorView = SOMActivityIndicatorView() + let loadingIndicatorView = SOMLoadingIndicatorView() private(set) var isEndEditingWhenWillDisappear: Bool = true @@ -58,6 +61,11 @@ class BaseViewController: UIViewController { $0.centerX.equalTo(self.view.safeAreaLayoutGuide.snp.centerX) $0.centerY.equalTo(self.view.safeAreaLayoutGuide.snp.centerY) } + + self.view.addSubview(self.loadingIndicatorView) + self.loadingIndicatorView.snp.makeConstraints { + $0.edges.equalToSuperview() + } self.bind() diff --git a/SOOUM/SOOUM/Base/DIContainer/BaseAssembler.swift b/SOOUM/SOOUM/Base/DIContainer/BaseAssembler.swift new file mode 100644 index 00000000..e4cb1c70 --- /dev/null +++ b/SOOUM/SOOUM/Base/DIContainer/BaseAssembler.swift @@ -0,0 +1,30 @@ +// +// BaseAssembler.swift +// SOOUM +// +// Created by 오현식 on 9/17/25. +// + +import Foundation + +/// 여러 의존성 등록 로직을 한 곳에 모아 관리하는 책임을 가집니다. +protocol BaseAssemblerable: AnyObject { + /// 추가 의존성을 `rootContainer`에 등록합니다. + /// - Parameter container: 의존성을 등록 혹은 반환합니다. + /// + /// ``` + /// DataSource: container.register(AnyDataSource.self) { _ in + /// AnyDataSourceImpl() + /// } + /// Repository: container.register(AnyRespository.self) { resolver in + /// AnyRespositoryImpl(resolver.resolve(AnyDataSource.self)) + /// } + /// UseCase: container.register(AnyUseCase.self) { resolver in + /// AnyUseCaseImpl(resolver.resolve(AnyRespository.self)) + /// } + /// Reactor: container.register(AnyReactor.self) { resolver in + /// AnyReactor(resolver.resolve(AnyUseCase.self)) + /// } + /// ``` + func assemble(container: BaseDIContainerable) +} diff --git a/SOOUM/SOOUM/Base/DIContainer/BaseDIContainer.swift b/SOOUM/SOOUM/Base/DIContainer/BaseDIContainer.swift new file mode 100644 index 00000000..7a1251f5 --- /dev/null +++ b/SOOUM/SOOUM/Base/DIContainer/BaseDIContainer.swift @@ -0,0 +1,65 @@ +// +// BaseDIContainer.swift +// SOOUM +// +// Created by 오현식 on 9/17/25. +// + +import Foundation + +/// DI 컨테이너가 수행해야 할 기능을 정의하는 프로토콜입니다. +/// `register`: 의존성을 등록합니다. +/// `resolve`: 등록된 의존성을 반환합니다. +protocol BaseDIContainerable: AnyObject { + + /// 서비스 타입과 해당 서비스를 생성하는 클로저(factory)를 등록합니다. + /// - Parameters: + /// - type: 등록할 서비스의 프로토콜 타입입니다. + /// - factory: 서비스를 생성하는 클로저입니다. 이 클로저는 자기 자신(컨테이너)을 파라미터로 받아, + /// 다른 의존성을 해결(resolve)하는 데 사용할 수 있습니다. + func register(_ type: Service.Type, factory: @escaping (BaseDIContainerable) -> Service) + + /// 등록된 서비스 타입의 인스턴스를 반환합니다. + /// - Parameter type: 해결(resolve)하려는 서비스의 프로토콜 타입입니다. + /// - Returns: 등록된 서비스의 인스턴스를 반환합니다. 만약 등록되지 않았다면 앱이 강제 종료됩니다. (개발 단계에서 의존성 설정 오류를 빠르게 파악하기 위함) + func resolve(_ type: Service.Type) -> Service +} + +/// `BaseDIContainerable`의 실제 구현 클래스입니다. +final class BaseDIContainer: BaseDIContainerable { + + // 부모 컨테이너에 대한 참조입니다. + private let parent: BaseDIContainerable? + // 등록된 서비스의 생성 클로저(factory)를 저장하는 딕셔너리입니다. + // 키는 서비스 타입의 이름(String), 값은 Any를 반환하는 클로저입니다. + private var factories: [String: (BaseDIContainerable) -> Any] = [:] + + /// 초기화 시 부모 컨테이너를 주입받을 수 있습니다. + /// - Parameter parent: 부모 컨테이너. nil일 경우, 최상위 컨테이너가 됩니다. + init(_ parent: BaseDIContainerable? = nil) { + self.parent = parent + } + + func register(_ type: Service.Type, factory: @escaping (BaseDIContainerable) -> Service) { + let key = String(describing: type) + self.factories[key] = factory + } + + func resolve(_ type: Service.Type) -> Service { + let key = String(describing: type) + // 1. 현재 컨테이너에서 의존성 해결을 시도합니다. + if let factory = self.factories[key] { + // factory는 (DIContainerProtocol) -> Any 타입을 가지므로, + // 실제 서비스 타입(Service)으로 캐스팅하여 반환합니다. + // register 함수에서 타입을 보장하므로 강제 캐스팅(!)이 안전합니다. + return factory(self) as! Service + } + // 2. 현재 컨테이너에서 찾지 못했고, 부모가 있다면 부모에게 해결을 위임합니다. + if let parent = self.parent { + return parent.resolve(type) + } + + // 해당 의존성이 등록되지 않은 경우, 개발자가 실수를 바로 인지할 수 있도록 fatalError를 발생시킵니다. + fatalError("Dependency for \(key) not registered.") + } +} diff --git a/SOOUM/SOOUM/Managers/AuthManager/AuthManager.swift b/SOOUM/SOOUM/Data/Managers/AuthManager/AuthManager.swift similarity index 57% rename from SOOUM/SOOUM/Managers/AuthManager/AuthManager.swift rename to SOOUM/SOOUM/Data/Managers/AuthManager/AuthManager.swift index 6098360d..01a8ebb7 100644 --- a/SOOUM/SOOUM/Managers/AuthManager/AuthManager.swift +++ b/SOOUM/SOOUM/Data/Managers/AuthManager/AuthManager.swift @@ -24,9 +24,11 @@ protocol AuthManagerDelegate: AnyObject { var hasToken: Bool { get } func convertPEMToSecKey(pemString: String) -> SecKey? func encryptUUIDWithPublicKey(publicKey: SecKey) -> String? - func join() -> Observable + func publicKey() -> Observable + func available() -> Observable + func join(nickname: String, profileImageName: String?) -> Observable func certification() -> Observable - func reAuthenticate(_ accessToken: String, _ completion: @escaping (AuthResult) -> Void) + func reAuthenticate(_ token: Token, _ completion: @escaping (AuthResult) -> Void) func initializeAuthInfo() func updateTokens(_ token: Token) func authPayloadByAccess() -> [String: String] @@ -106,63 +108,90 @@ extension AuthManager: AuthManagerDelegate { // MARK: Account Verification - func join() -> Observable { + func publicKey() -> Observable { - guard let provider = self.provider else { return .just(false) } + guard let provider = self.provider else { return .just(nil) } - return provider.networkManager.request(RSAKeyResponse.self, request: AuthRequest.getPublicKey) + let request: AuthRequest = .publicKey + return provider.networkManager.fetch(KeyInfoResponse.self, request: request) .map(\.publicKey) + } + + func available() -> Observable { + + return self.publicKey() + .withUnretained(self) + .flatMapLatest { object, publicKey -> Observable in + + if let publicKey = publicKey, + let secKey = object.convertPEMToSecKey(pemString: publicKey), + let encryptedDeviceId = object.encryptUUIDWithPublicKey(publicKey: secKey), + let provider = object.provider { + + let request: UserRequest = .checkAvailable(encryptedDeviceId: encryptedDeviceId) + return provider.networkManager.perform(CheckAvailableResponse.self, request: request) + } else { + return .just(CheckAvailableResponse.emptyValue()) + } + } + } + + func join(nickname: String, profileImageName: String?) -> Observable { + + return self.publicKey() .withUnretained(self) .flatMapLatest { object, publicKey -> Observable in - if let secKey = object.convertPEMToSecKey(pemString: publicKey), - let encryptedDeviceId = object.encryptUUIDWithPublicKey(publicKey: secKey) { + if let publicKey = publicKey, + let secKey = object.convertPEMToSecKey(pemString: publicKey), + let encryptedDeviceId = object.encryptUUIDWithPublicKey(publicKey: secKey), + let provider = object.provider { let request: AuthRequest = .signUp( encryptedDeviceId: encryptedDeviceId, - isAllowNotify: true, - isAllowTermOne: true, - isAllowTermTwo: true, - isAllowTermThree: true + isNotificationAgreed: provider.pushManager.notificationStatus, + nickname: nickname, + profileImageName: profileImageName ) - return provider.networkManager.request(SignUpResponse.self, request: request) - .map { response in - object.authInfo.updateToken(response.token) + return provider.networkManager.perform(SignUpResponse.self, request: request) + .map(\.token) + .flatMapLatest { token -> Observable in + + // session token 업데이트 + object.authInfo.updateToken(token) // FCM token 업데이트 - object.provider?.networkManager.registerFCMToken(from: #function) - return true + provider.networkManager.registerFCMToken(from: #function) + return .just(true) } + } else { + return .just(false) } - return .just(false) } } func certification() -> Observable { - guard let provider = self.provider else { return .just(false) } - - return provider.networkManager.request(RSAKeyResponse.self, request: AuthRequest.getPublicKey) - .map(\.publicKey) + return self.publicKey() .withUnretained(self) .flatMapLatest { object, publicKey -> Observable in - - if let secKey = object.convertPEMToSecKey(pemString: publicKey), - let encryptedDeviceId = object.encryptUUIDWithPublicKey(publicKey: secKey) { + + if let publicKey = publicKey, + let secKey = object.convertPEMToSecKey(pemString: publicKey), + let encryptedDeviceId = object.encryptUUIDWithPublicKey(publicKey: secKey), + let provider = object.provider { let request: AuthRequest = .login(encryptedDeviceId: encryptedDeviceId) - return provider.networkManager.request(SignInResponse.self, request: request) - .map { response -> Bool in - if response.isRegistered, let token = response.token { - - object.authInfo.updateToken(token) - - // FCM token 업데이트 - object.provider?.networkManager.registerFCMToken(from: #function) - return true - } else { - return false - } + return provider.networkManager.perform(LoginResponse.self, request: request) + .map(\.token) + .flatMapLatest { token -> Observable in + + // session token 업데이트 + object.authInfo.updateToken(token) + + // FCM token 업데이트 + provider.networkManager.registerFCMToken(from: #function) + return .just(true) } } else { return .just(false) @@ -177,11 +206,9 @@ extension AuthManager: AuthManagerDelegate { 3. 재인증 완료된 후, 이전의 호출이 이전 토큰을 가지고 시도할 수 있기 때문에, 호출의 토큰과 현재 토큰이 같은 때만 통과시킨다 4. RefreshToken 도 유효하지 않다면 로그인 시도 */ - func reAuthenticate(_ accessToken: String, _ completion: @escaping (AuthResult) -> Void) { + func reAuthenticate(_ token: Token, _ completion: @escaping (AuthResult) -> Void) { - let token = self.authInfo.token - - guard token.refreshToken.isEmpty == false else { + guard self.authInfo.token.refreshToken.isEmpty == false else { let error = NSError( domain: "SOOUM", code: -99, @@ -199,7 +226,7 @@ extension AuthManager: AuthManagerDelegate { } /// AccessToken이 업데이트 됐다면, 즉시 성공 처리 - guard accessToken == token.accessToken else { + guard token.accessToken == self.authInfo.token.accessToken else { completion(.success) return } @@ -209,54 +236,52 @@ extension AuthManager: AuthManagerDelegate { guard let provider = self.provider else { return } - let request: AuthRequest = .reAuthenticationWithRefreshSession - provider.networkManager.request(ReAuthenticationResponse.self, request: request) - .map(\.accessToken) + let request: AuthRequest = .reAuthenticationWithRefreshSession(token: token) + provider.networkManager.perform(TokenResponse.self, request: request) + .map(\.token) + .withUnretained(self) + .flatMapLatest { object, token -> Observable in + + if token.accessToken.isEmpty || token.refreshToken.isEmpty { + let error = NSError( + domain: "SOOUM", + code: -99, + userInfo: [NSLocalizedDescriptionKey: "Session not refresh"] + ) + return .just(.failure(error)) + } else { + + object.updateTokens(token) + + // FCM token 업데이트 + provider.networkManager.registerFCMToken(from: #function) + + return .just(.success) + } + } + .catch { [weak self] error -> Observable in + + guard let self = self else { return .just(.failure(error))} + + let errorCode = (error as NSError).code + if errorCode == 403 { + + return self.certification() + .map { isRegistered -> AuthResult in + return isRegistered ? .success : .failure(error) + } + } else { + return .just(.failure(error)) + } + } .subscribe( with: self, - onNext: { object, accessToken in - if accessToken.isEmpty { - let error = NSError( - domain: "SOOUM", - code: -99, - userInfo: [NSLocalizedDescriptionKey: "Session not refresh"] - ) - object.excutePendingResults(.failure(error)) - } else { - - object.updateTokens( - .init( - accessToken: accessToken, - refreshToken: token.refreshToken - ) - ) - - // FCM token 업데이트 - object.provider?.networkManager.registerFCMToken(from: #function) - - object.excutePendingResults(.success) - } - + onNext: { object, result in + object.excutePendingResults(result) object.isReAuthenticating = false }, onError: { object, error in - - let errorCode = (error as NSError).code - switch errorCode { - case 403: - object.certification() - .subscribe(onNext: { isRegistered in - if isRegistered { - object.excutePendingResults(.success) - } else { - object.excutePendingResults(.failure(error)) - } - }) - .disposed(by: self.disposeBag) - default: - break - } - + object.excutePendingResults(.failure(error)) object.isReAuthenticating = false } ) diff --git a/SOOUM/SOOUM/Managers/AuthManager/AuthManagerConfiguration.swift b/SOOUM/SOOUM/Data/Managers/AuthManager/AuthManagerConfiguration.swift similarity index 100% rename from SOOUM/SOOUM/Managers/AuthManager/AuthManagerConfiguration.swift rename to SOOUM/SOOUM/Data/Managers/AuthManager/AuthManagerConfiguration.swift diff --git a/SOOUM/SOOUM/Managers/AuthManager/Models/AuthInfo.swift b/SOOUM/SOOUM/Data/Managers/AuthManager/Models/AuthInfo.swift similarity index 100% rename from SOOUM/SOOUM/Managers/AuthManager/Models/AuthInfo.swift rename to SOOUM/SOOUM/Data/Managers/AuthManager/Models/AuthInfo.swift diff --git a/SOOUM/SOOUM/Managers/CompositeManager.swift b/SOOUM/SOOUM/Data/Managers/CompositeManager.swift similarity index 100% rename from SOOUM/SOOUM/Managers/CompositeManager.swift rename to SOOUM/SOOUM/Data/Managers/CompositeManager.swift diff --git a/SOOUM/SOOUM/Managers/GAManager/AnalyticsEventProtocol.swift b/SOOUM/SOOUM/Data/Managers/GAManager/AnalyticsEventProtocol.swift similarity index 100% rename from SOOUM/SOOUM/Managers/GAManager/AnalyticsEventProtocol.swift rename to SOOUM/SOOUM/Data/Managers/GAManager/AnalyticsEventProtocol.swift diff --git a/SOOUM/SOOUM/Managers/GAManager/FirebaseLoggable.swift b/SOOUM/SOOUM/Data/Managers/GAManager/FirebaseLoggable.swift similarity index 100% rename from SOOUM/SOOUM/Managers/GAManager/FirebaseLoggable.swift rename to SOOUM/SOOUM/Data/Managers/GAManager/FirebaseLoggable.swift diff --git a/SOOUM/SOOUM/Managers/GAManager/GAManager.swift b/SOOUM/SOOUM/Data/Managers/GAManager/GAManager.swift similarity index 100% rename from SOOUM/SOOUM/Managers/GAManager/GAManager.swift rename to SOOUM/SOOUM/Data/Managers/GAManager/GAManager.swift diff --git a/SOOUM/SOOUM/Managers/GAManager/SOMEvent.swift b/SOOUM/SOOUM/Data/Managers/GAManager/SOMEvent.swift similarity index 100% rename from SOOUM/SOOUM/Managers/GAManager/SOMEvent.swift rename to SOOUM/SOOUM/Data/Managers/GAManager/SOMEvent.swift diff --git a/SOOUM/SOOUM/Managers/LocationManager/LocationManager.swift b/SOOUM/SOOUM/Data/Managers/LocationManager/LocationManager.swift similarity index 100% rename from SOOUM/SOOUM/Managers/LocationManager/LocationManager.swift rename to SOOUM/SOOUM/Data/Managers/LocationManager/LocationManager.swift diff --git a/SOOUM/SOOUM/Managers/LocationManager/LocationManagerConfigruation.swift b/SOOUM/SOOUM/Data/Managers/LocationManager/LocationManagerConfigruation.swift similarity index 100% rename from SOOUM/SOOUM/Managers/LocationManager/LocationManagerConfigruation.swift rename to SOOUM/SOOUM/Data/Managers/LocationManager/LocationManagerConfigruation.swift diff --git a/SOOUM/SOOUM/Managers/LocationManager/Models/Coordinate.swift b/SOOUM/SOOUM/Data/Managers/LocationManager/Models/Coordinate.swift similarity index 100% rename from SOOUM/SOOUM/Managers/LocationManager/Models/Coordinate.swift rename to SOOUM/SOOUM/Data/Managers/LocationManager/Models/Coordinate.swift diff --git a/SOOUM/SOOUM/Managers/ManagerConfiguration.swift b/SOOUM/SOOUM/Data/Managers/ManagerConfiguration.swift similarity index 100% rename from SOOUM/SOOUM/Managers/ManagerConfiguration.swift rename to SOOUM/SOOUM/Data/Managers/ManagerConfiguration.swift diff --git a/SOOUM/SOOUM/Managers/ManagerProvider.swift b/SOOUM/SOOUM/Data/Managers/ManagerProvider.swift similarity index 100% rename from SOOUM/SOOUM/Managers/ManagerProvider.swift rename to SOOUM/SOOUM/Data/Managers/ManagerProvider.swift diff --git a/SOOUM/SOOUM/Managers/ManagerType.swift b/SOOUM/SOOUM/Data/Managers/ManagerType.swift similarity index 100% rename from SOOUM/SOOUM/Managers/ManagerType.swift rename to SOOUM/SOOUM/Data/Managers/ManagerType.swift diff --git a/SOOUM/SOOUM/Managers/NetworkManager/DefinedError.swift b/SOOUM/SOOUM/Data/Managers/NetworkManager/DefinedError.swift similarity index 83% rename from SOOUM/SOOUM/Managers/NetworkManager/DefinedError.swift rename to SOOUM/SOOUM/Data/Managers/NetworkManager/DefinedError.swift index c4adae21..0d018ec7 100644 --- a/SOOUM/SOOUM/Managers/NetworkManager/DefinedError.swift +++ b/SOOUM/SOOUM/Data/Managers/NetworkManager/DefinedError.swift @@ -7,14 +7,18 @@ import Foundation +import Alamofire + enum DefinedError: Error, LocalizedError { case badRequest case unauthorized case payment case forbidden + case notFound case teapot case locked + case invalidMethod(HTTPMethod) case unknown(Int) static func error(with statusCode: Int) -> Self { @@ -27,6 +31,8 @@ enum DefinedError: Error, LocalizedError { return .payment case 403: return .forbidden + case 404: + return .notFound case 418: return .teapot case 423: @@ -46,10 +52,14 @@ enum DefinedError: Error, LocalizedError { return "Delete parent card: HTTP 402 received." case .forbidden: return "Expire RefreshToken: HTTP 403 received." + case .notFound: + return "Not Found: HTTP 404 received" case .teapot: return "Stop using RefreshToken: HTTP 418 received." case .locked: return "LOCKED: HTTP 423 received." + case let .invalidMethod(httpMethod): + return "Invalid Method: HTTPMethod \(httpMethod) was not expected" case let .unknown(statusCode): return "Unknown error: HTTP \(statusCode) received." } @@ -61,8 +71,10 @@ enum DefinedError: Error, LocalizedError { case .unauthorized: 401 case .payment: 402 case .forbidden: 403 + case .notFound: 404 case .teapot: 418 case .locked: 423 + case .invalidMethod: -99 case let .unknown(statusCode): statusCode } diff --git a/SOOUM/SOOUM/Managers/NetworkManager/Interceptor/AddingTokenInterceptor.swift b/SOOUM/SOOUM/Data/Managers/NetworkManager/Interceptor/AddingTokenInterceptor.swift similarity index 100% rename from SOOUM/SOOUM/Managers/NetworkManager/Interceptor/AddingTokenInterceptor.swift rename to SOOUM/SOOUM/Data/Managers/NetworkManager/Interceptor/AddingTokenInterceptor.swift diff --git a/SOOUM/SOOUM/Managers/NetworkManager/Interceptor/CompositeInterceptor.swift b/SOOUM/SOOUM/Data/Managers/NetworkManager/Interceptor/CompositeInterceptor.swift similarity index 96% rename from SOOUM/SOOUM/Managers/NetworkManager/Interceptor/CompositeInterceptor.swift rename to SOOUM/SOOUM/Data/Managers/NetworkManager/Interceptor/CompositeInterceptor.swift index 79faa3b9..c7eacf35 100644 --- a/SOOUM/SOOUM/Managers/NetworkManager/Interceptor/CompositeInterceptor.swift +++ b/SOOUM/SOOUM/Data/Managers/NetworkManager/Interceptor/CompositeInterceptor.swift @@ -12,13 +12,11 @@ import Alamofire class CompositeInterceptor: RequestInterceptor { - private let provider: ManagerTypeDelegate private let interceptors: [RequestInterceptor] private let timeoutInterval: TimeInterval = 20.0 init(provider: ManagerTypeDelegate) { - self.provider = provider self.interceptors = [ AddingTokenInterceptor(provider: provider), diff --git a/SOOUM/SOOUM/Managers/NetworkManager/Interceptor/ErrorInterceptor.swift b/SOOUM/SOOUM/Data/Managers/NetworkManager/Interceptor/ErrorInterceptor.swift similarity index 89% rename from SOOUM/SOOUM/Managers/NetworkManager/Interceptor/ErrorInterceptor.swift rename to SOOUM/SOOUM/Data/Managers/NetworkManager/Interceptor/ErrorInterceptor.swift index 8ef83398..9921a230 100644 --- a/SOOUM/SOOUM/Managers/NetworkManager/Interceptor/ErrorInterceptor.swift +++ b/SOOUM/SOOUM/Data/Managers/NetworkManager/Interceptor/ErrorInterceptor.swift @@ -38,8 +38,8 @@ class ErrorInterceptor: RequestInterceptor { return } - let accessToken = self.provider.authManager.authInfo.token.accessToken - self.provider.authManager.reAuthenticate(accessToken) { result in + let token = self.provider.authManager.authInfo.token + self.provider.authManager.reAuthenticate(token) { result in switch result { case .success: diff --git a/SOOUM/SOOUM/Managers/NetworkManager/Interceptor/TimeoutInterceptor.swift b/SOOUM/SOOUM/Data/Managers/NetworkManager/Interceptor/TimeoutInterceptor.swift similarity index 100% rename from SOOUM/SOOUM/Managers/NetworkManager/Interceptor/TimeoutInterceptor.swift rename to SOOUM/SOOUM/Data/Managers/NetworkManager/Interceptor/TimeoutInterceptor.swift diff --git a/SOOUM/SOOUM/Managers/NetworkManager/Models/PushTokenSet.swift b/SOOUM/SOOUM/Data/Managers/NetworkManager/Models/PushTokenSet.swift similarity index 100% rename from SOOUM/SOOUM/Managers/NetworkManager/Models/PushTokenSet.swift rename to SOOUM/SOOUM/Data/Managers/NetworkManager/Models/PushTokenSet.swift diff --git a/SOOUM/SOOUM/Managers/NetworkManager/Monitor/LogginMonitor.swift b/SOOUM/SOOUM/Data/Managers/NetworkManager/Monitor/LogginMonitor.swift similarity index 100% rename from SOOUM/SOOUM/Managers/NetworkManager/Monitor/LogginMonitor.swift rename to SOOUM/SOOUM/Data/Managers/NetworkManager/Monitor/LogginMonitor.swift diff --git a/SOOUM/SOOUM/Managers/NetworkManager/NetworkManager.swift b/SOOUM/SOOUM/Data/Managers/NetworkManager/NetworkManager.swift similarity index 66% rename from SOOUM/SOOUM/Managers/NetworkManager/NetworkManager.swift rename to SOOUM/SOOUM/Data/Managers/NetworkManager/NetworkManager.swift index 9d167ff3..7dfaa1a3 100644 --- a/SOOUM/SOOUM/Managers/NetworkManager/NetworkManager.swift +++ b/SOOUM/SOOUM/Data/Managers/NetworkManager/NetworkManager.swift @@ -8,7 +8,6 @@ import Foundation import Alamofire -import FirebaseMessaging import RxSwift @@ -20,8 +19,14 @@ protocol NetworkManagerDelegate: AnyObject { to url: URLConvertible ) -> Observable> + func fetch(_ object: T.Type, request: BaseRequest) -> Observable + func perform(_ object: T.Type, request: BaseRequest) -> Observable + func registerFCMToken(with tokenSet: PushTokenSet, _ function: String) func registerFCMToken(from function: String) + + func version() -> Observable> + func updateCheck() -> Observable } class NetworkManager: CompositeManager { @@ -119,61 +124,22 @@ extension NetworkManager: NetworkManagerDelegate { } } } -} - - -// MARK: Register FCM token - -extension NetworkManager { - - static var registeredToken: PushTokenSet? - static var fcmDisposeBag = DisposeBag() - func registerFCMToken(with tokenSet: PushTokenSet, _ function: String) { + func fetch(_ object: T.Type, request: BaseRequest) -> Observable { - // AccessToken이 없는 경우 업데이트에 실패하므로 무시 - guard let provider = self.provider, provider.authManager.hasToken else { - Log.info("Can't upload fcm token without authorization token. (from: \(function))") - return + guard request.method == .get else { + return Observable.error(DefinedError.invalidMethod(request.method)) } - - let prevTokenSet: PushTokenSet? = Self.registeredToken - // TODO: 이전에 업로드 성공한 토큰이 다시 등록되는 경우 무시, 계정 이관 이슈로 중복 토큰도 항상 업데이트 - // guard tokenSet != Self.registeredToken else { - // Log.info("Ignored already registered token set. (from: \(`func`))") - // return - // } - - guard let fcmToken = tokenSet.fcm, let apns = tokenSet.apns else { return } - Log.info("Firebase registration token: \(fcmToken) [with \(apns)] (from: \(function))") - - // 서버에 FCM token 등록 - if let fcmToken = tokenSet.fcm, let provider = self.provider { - - let request: AuthRequest = .updateFCM(fcmToken: fcmToken) - provider.networkManager.request(Empty.self, request: request) - .subscribe( - onNext: { _ in - Log.info("Update FCM token to server with", fcmToken) - }, - onError: { _ in - Log.error("Failed to update FCM token to server: not found user") - } - ) - .disposed(by: Self.fcmDisposeBag) - } else { - - Self.registeredToken = prevTokenSet - Log.info("Failed to update FCM token to server: not found device unique id") - } + return self.request(object, request: request) } - func registerFCMToken(from func: String) { - let tokenSet = PushTokenSet( - apns: nil, - fcm: Messaging.messaging().fcmToken - ) - self.registerFCMToken(with: tokenSet, `func`) + func perform(_ object: T.Type, request: BaseRequest) -> Observable { + + guard request.method == .post || request.method == .patch || request.method == .delete else { + return Observable.error(DefinedError.invalidMethod(request.method)) + } + + return self.request(object, request: request) } } diff --git a/SOOUM/SOOUM/Managers/NetworkManager/NetworkManagerConfiguration.swift b/SOOUM/SOOUM/Data/Managers/NetworkManager/NetworkManagerConfiguration.swift similarity index 81% rename from SOOUM/SOOUM/Managers/NetworkManager/NetworkManagerConfiguration.swift rename to SOOUM/SOOUM/Data/Managers/NetworkManager/NetworkManagerConfiguration.swift index 88235bba..0f561388 100644 --- a/SOOUM/SOOUM/Managers/NetworkManager/NetworkManagerConfiguration.swift +++ b/SOOUM/SOOUM/Data/Managers/NetworkManager/NetworkManagerConfiguration.swift @@ -38,16 +38,10 @@ struct NetworkManagerConfiguration: ManagerConfiguration { self.sessionDelegate = sessionDelegate self.sessionDelegateQueue = sessionDelegateQueue - let formatter = DateFormatter() - formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSSSS" - /// UTC 기준 - formatter.timeZone = .Korea - formatter.locale = .Korea - self.decoder = JSONDecoder() - self.decoder.dateDecodingStrategy = .formatted(formatter) + self.decoder.dateDecodingStrategy = .iso8601 self.encoder = JSONEncoder() - self.encoder.dateEncodingStrategy = .formatted(formatter) + self.encoder.dateEncodingStrategy = .iso8601 } } diff --git a/SOOUM/SOOUM/Data/Managers/NetworkManager/NetworkManager_FCM.swift b/SOOUM/SOOUM/Data/Managers/NetworkManager/NetworkManager_FCM.swift new file mode 100644 index 00000000..c8a0fe94 --- /dev/null +++ b/SOOUM/SOOUM/Data/Managers/NetworkManager/NetworkManager_FCM.swift @@ -0,0 +1,69 @@ +// +// NetworkManager_FCM.swift +// SOOUM +// +// Created by 오현식 on 9/16/25. +// + +import Foundation + +import Alamofire +import FirebaseMessaging +import RxSwift + + +// MARK: Register FCM token + +extension NetworkManager { + + static var registeredToken: PushTokenSet? + static var fcmDisposeBag = DisposeBag() + + func registerFCMToken(with tokenSet: PushTokenSet, _ function: String) { + + // AccessToken이 없는 경우 업데이트에 실패하므로 무시 + guard let provider = self.provider, provider.authManager.hasToken else { + Log.info("Can't upload fcm token without authorization token. (from: \(function))") + return + } + + + let prevTokenSet: PushTokenSet? = Self.registeredToken + // TODO: 이전에 업로드 성공한 토큰이 다시 등록되는 경우 무시, 계정 이관 이슈로 중복 토큰도 항상 업데이트 + // guard tokenSet != Self.registeredToken else { + // Log.info("Ignored already registered token set. (from: \(`func`))") + // return + // } + + guard let fcmToken = tokenSet.fcm, let apns = tokenSet.apns else { return } + Log.info("Firebase registration token: \(fcmToken) [with \(apns)] (from: \(function))") + + // 서버에 FCM token 등록 + if let fcmToken = tokenSet.fcm { + + let request: UserRequest = .updateFCMToken(fcmToken: fcmToken) + provider.networkManager.perform(Empty.self, request: request) + .subscribe( + onNext: { _ in + Log.info("Update FCM token to server with", fcmToken) + }, + onError: { _ in + Log.error("Failed to update FCM token to server: not found user") + } + ) + .disposed(by: Self.fcmDisposeBag) + } else { + + Self.registeredToken = prevTokenSet + Log.info("Failed to update FCM token to server: not found device unique id") + } + } + + func registerFCMToken(from func: String) { + let tokenSet = PushTokenSet( + apns: nil, + fcm: Messaging.messaging().fcmToken + ) + self.registerFCMToken(with: tokenSet, `func`) + } +} diff --git a/SOOUM/SOOUM/Data/Managers/NetworkManager/NetworkManager_Version.swift b/SOOUM/SOOUM/Data/Managers/NetworkManager/NetworkManager_Version.swift new file mode 100644 index 00000000..9d023e30 --- /dev/null +++ b/SOOUM/SOOUM/Data/Managers/NetworkManager/NetworkManager_Version.swift @@ -0,0 +1,30 @@ +// +// NetworkManager_Version.swift +// SOOUM +// +// Created by 오현식 on 9/16/25. +// + +import Foundation + +import Alamofire +import RxSwift + + +// MARK: Version + +extension NetworkManager { + + func version() -> Observable> { + + let request = VersionRequest.version + return self.fetch(AppVersionStatusResponse.self, request: request) + .map { return .success($0) } + .catch { return .just(.failure($0)) } + .observe(on: MainScheduler.instance) + } + + func updateCheck() -> Observable { + return self.version().map { (try? $0.get()) ?? AppVersionStatusResponse.emptyValue() } + } +} diff --git a/SOOUM/SOOUM/Managers/PushManager/Models/NotificationInfo.swift b/SOOUM/SOOUM/Data/Managers/PushManager/Models/NotificationInfo.swift similarity index 100% rename from SOOUM/SOOUM/Managers/PushManager/Models/NotificationInfo.swift rename to SOOUM/SOOUM/Data/Managers/PushManager/Models/NotificationInfo.swift diff --git a/SOOUM/SOOUM/Managers/PushManager/PushManager+Rx.swift b/SOOUM/SOOUM/Data/Managers/PushManager/PushManager+Rx.swift similarity index 80% rename from SOOUM/SOOUM/Managers/PushManager/PushManager+Rx.swift rename to SOOUM/SOOUM/Data/Managers/PushManager/PushManager+Rx.swift index 80bc2f51..03b95046 100644 --- a/SOOUM/SOOUM/Managers/PushManager/PushManager+Rx.swift +++ b/SOOUM/SOOUM/Data/Managers/PushManager/PushManager+Rx.swift @@ -11,8 +11,8 @@ import RxSwift extension PushManagerDelegate { func switchNotification(on: Bool) -> Observable { - return .create { observer in - self.switchNotification(isOn: on) { error in + return Observable.create { [weak self] observer in + self?.switchNotification(isOn: on) { error in if let error: Error = error { observer.onNext(error) } else { diff --git a/SOOUM/SOOUM/Managers/PushManager/PushManager.swift b/SOOUM/SOOUM/Data/Managers/PushManager/PushManager.swift similarity index 77% rename from SOOUM/SOOUM/Managers/PushManager/PushManager.swift rename to SOOUM/SOOUM/Data/Managers/PushManager/PushManager.swift index 4ed0f191..71573db9 100644 --- a/SOOUM/SOOUM/Managers/PushManager/PushManager.swift +++ b/SOOUM/SOOUM/Data/Managers/PushManager/PushManager.swift @@ -11,7 +11,7 @@ import UIKit protocol PushManagerDelegate: AnyObject { var window: UIWindow? { get } - func setupRootViewController(_ info: NotificationInfo?, terminated: Bool) + // func setupRootViewController(_ info: NotificationInfo?, terminated: Bool) var canReceiveNotifications: Bool { get } var notificationStatus: Bool { get } @@ -47,44 +47,46 @@ extension PushManager: PushManagerDelegate { // MARK: Navigation - func setupRootViewController(_ info: NotificationInfo?, terminated: Bool) { - - DispatchQueue.main.async { [weak self] in - if self?.window != nil { - if terminated { - self?.setupLaunchScreenViewController(info) - } else { - self?.setupMainTabBarController(info) - } - } - } - } + // func setupRootViewController(_ info: NotificationInfo?, terminated: Bool) { + // + // DispatchQueue.main.async { [weak self] in + // if self?.window != nil { + // if terminated { + // self?.setupLaunchScreenViewController(info) + // } else { + // self?.setupMainTabBarController(info) + // } + // } + // } + // } - fileprivate func setupLaunchScreenViewController(_ pushInfo: NotificationInfo?) { - - guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else { return } - let provider = appDelegate.provider - - let launchScreenReactor = LaunchScreenViewReactor(provider: provider, pushInfo: pushInfo) - let launchScreenViewController = LaunchScreenViewController() - launchScreenViewController.reactor = launchScreenReactor - - let navigationController = UINavigationController(rootViewController: launchScreenViewController) - self.window?.rootViewController = navigationController - } + // fileprivate func setupLaunchScreenViewController(_ pushInfo: NotificationInfo?) { + // + // guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else { return } + // let appDIContainer = appDelegate.appDIContainer + // + // _ = appDIContainer.setupLaunchContainer(with: pushInfo) + // + // let launchScreenReactor = LaunchScreenViewReactor(provider: provider, pushInfo: pushInfo) + // let launchScreenViewController = LaunchScreenViewController() + // launchScreenViewController.reactor = launchScreenReactor + // + // let navigationController = UINavigationController(rootViewController: launchScreenViewController) + // self.window?.rootViewController = navigationController + // } - fileprivate func setupMainTabBarController(_ pushInfo: NotificationInfo?) { - - guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else { return } - let provider = appDelegate.provider - - let mainTabBarReactor = MainTabBarReactor(provider: provider, pushInfo: pushInfo) - let mainTabBarController = MainTabBarController() - mainTabBarController.reactor = mainTabBarReactor - - let navigationController = UINavigationController(rootViewController: mainTabBarController) - self.window?.rootViewController = navigationController - } + // fileprivate func setupMainTabBarController(_ pushInfo: NotificationInfo?) { + // + // guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else { return } + // let provider = appDelegate.provider + // + // let mainTabBarReactor = MainTabBarReactor(provider: provider, pushInfo: pushInfo) + // let mainTabBarController = MainTabBarController() + // mainTabBarController.reactor = mainTabBarReactor + // + // let navigationController = UINavigationController(rootViewController: mainTabBarController) + // self.window?.rootViewController = navigationController + // } // MARK: Notification diff --git a/SOOUM/SOOUM/Managers/PushManager/PushManagerConfiguration.swift b/SOOUM/SOOUM/Data/Managers/PushManager/PushManagerConfiguration.swift similarity index 100% rename from SOOUM/SOOUM/Managers/PushManager/PushManagerConfiguration.swift rename to SOOUM/SOOUM/Data/Managers/PushManager/PushManagerConfiguration.swift diff --git a/SOOUM/SOOUM/Data/Models/Responses/AppVersionStatusResponse.swift b/SOOUM/SOOUM/Data/Models/Responses/AppVersionStatusResponse.swift new file mode 100644 index 00000000..390bf185 --- /dev/null +++ b/SOOUM/SOOUM/Data/Models/Responses/AppVersionStatusResponse.swift @@ -0,0 +1,28 @@ +// +// AppVersionStatusResponse.swift +// SOOUM +// +// Created by 오현식 on 9/16/25. +// + +import Alamofire + +struct AppVersionStatusResponse { + + let version: Version +} + +extension AppVersionStatusResponse: EmptyResponse { + + static func emptyValue() -> AppVersionStatusResponse { + return AppVersionStatusResponse(version: Version.defaultValue) + } +} + +extension AppVersionStatusResponse: Decodable { + + init(from decoder: any Decoder) throws { + let singleContainer = try decoder.singleValueContainer() + self.version = try singleContainer.decode(Version.self) + } +} diff --git a/SOOUM/SOOUM/Data/Models/Responses/BlockedNotificationInfoResponse.swift b/SOOUM/SOOUM/Data/Models/Responses/BlockedNotificationInfoResponse.swift new file mode 100644 index 00000000..ce3fafea --- /dev/null +++ b/SOOUM/SOOUM/Data/Models/Responses/BlockedNotificationInfoResponse.swift @@ -0,0 +1,40 @@ +// +// BlockedNotificationInfoResponse.swift +// SOOUM +// +// Created by 오현식 on 9/16/25. +// + +import Alamofire + +struct BlockedNotificationInfoResponse { + + let notificationInfo: CommonNotificationInfo + let blockExpirationDateTime: Date +} + +extension BlockedNotificationInfoResponse: EmptyResponse { + + static func emptyValue() -> BlockedNotificationInfoResponse { + BlockedNotificationInfoResponse( + notificationInfo: CommonNotificationInfo.defaultValue, + blockExpirationDateTime: Date() + ) + } +} + +extension BlockedNotificationInfoResponse: Decodable { + + enum CodingKeys: CodingKey { + case notificationInfo + case blockExpirationDateTime + } + + init(from decoder: any Decoder) throws { + let singleContainer = try decoder.singleValueContainer() + self.notificationInfo = try singleContainer.decode(CommonNotificationInfo.self) + + let container = try decoder.container(keyedBy: CodingKeys.self) + self.blockExpirationDateTime = try container.decode(Date.self, forKey: .blockExpirationDateTime) + } +} diff --git a/SOOUM/SOOUM/Data/Models/Responses/CheckAvailableResponse.swift b/SOOUM/SOOUM/Data/Models/Responses/CheckAvailableResponse.swift new file mode 100644 index 00000000..82684d86 --- /dev/null +++ b/SOOUM/SOOUM/Data/Models/Responses/CheckAvailableResponse.swift @@ -0,0 +1,28 @@ +// +// CheckAvailableResponse.swift +// SOOUM +// +// Created by 오현식 on 9/16/25. +// + +import Alamofire + +struct CheckAvailableResponse { + + let checkAvailable: CheckAvailable +} + +extension CheckAvailableResponse: EmptyResponse { + + static func emptyValue() -> CheckAvailableResponse { + CheckAvailableResponse(checkAvailable: CheckAvailable.defaultValue) + } +} + +extension CheckAvailableResponse: Decodable { + + init(from decoder: any Decoder) throws { + let singleContainer = try decoder.singleValueContainer() + self.checkAvailable = try singleContainer.decode(CheckAvailable.self) + } +} diff --git a/SOOUM/SOOUM/Data/Models/Responses/DeleteNotificationInfoResponse.swift b/SOOUM/SOOUM/Data/Models/Responses/DeleteNotificationInfoResponse.swift new file mode 100644 index 00000000..39b0a993 --- /dev/null +++ b/SOOUM/SOOUM/Data/Models/Responses/DeleteNotificationInfoResponse.swift @@ -0,0 +1,28 @@ +// +// DeleteNotificationInfoResponse.swift +// SOOUM +// +// Created by 오현식 on 9/16/25. +// + +import Alamofire + +struct DeleteNotificationInfoResponse { + + let notificationInfo: CommonNotificationInfo +} + +extension DeleteNotificationInfoResponse: EmptyResponse { + + static func emptyValue() -> DeleteNotificationInfoResponse { + DeleteNotificationInfoResponse(notificationInfo: CommonNotificationInfo.defaultValue) + } +} + +extension DeleteNotificationInfoResponse: Decodable { + + init(from decoder: any Decoder) throws { + let singleContainer = try decoder.singleValueContainer() + self.notificationInfo = try singleContainer.decode(CommonNotificationInfo.self) + } +} diff --git a/SOOUM/SOOUM/Data/Models/Responses/ImageUrlInfoResponse.swift b/SOOUM/SOOUM/Data/Models/Responses/ImageUrlInfoResponse.swift new file mode 100644 index 00000000..4457b6b2 --- /dev/null +++ b/SOOUM/SOOUM/Data/Models/Responses/ImageUrlInfoResponse.swift @@ -0,0 +1,28 @@ +// +// ImageUrlInfoResponse.swift +// SOOUM +// +// Created by 오현식 on 9/16/25. +// + +import Alamofire + +struct ImageUrlInfoResponse { + + let imageUrlInfo: ImageUrlInfo +} + +extension ImageUrlInfoResponse: EmptyResponse { + + static func emptyValue() -> ImageUrlInfoResponse { + ImageUrlInfoResponse(imageUrlInfo: ImageUrlInfo.defaultValue) + } +} + +extension ImageUrlInfoResponse: Decodable { + + init(from decoder: any Decoder) throws { + let singleContainer = try decoder.singleValueContainer() + self.imageUrlInfo = try singleContainer.decode(ImageUrlInfo.self) + } +} diff --git a/SOOUM/SOOUM/Data/Models/Responses/KeyInfoResponse.swift b/SOOUM/SOOUM/Data/Models/Responses/KeyInfoResponse.swift new file mode 100644 index 00000000..6d462a29 --- /dev/null +++ b/SOOUM/SOOUM/Data/Models/Responses/KeyInfoResponse.swift @@ -0,0 +1,13 @@ +// +// KeyInfoResponse.swift +// SOOUM +// +// Created by 오현식 on 9/16/25. +// + +import Alamofire + +struct KeyInfoResponse: Decodable { + + let publicKey: String +} diff --git a/SOOUM/SOOUM/Data/Models/Responses/LoginResponse.swift b/SOOUM/SOOUM/Data/Models/Responses/LoginResponse.swift new file mode 100644 index 00000000..a782ef41 --- /dev/null +++ b/SOOUM/SOOUM/Data/Models/Responses/LoginResponse.swift @@ -0,0 +1,32 @@ +// +// LoginResponse.swift +// SOOUM +// +// Created by 오현식 on 9/16/25. +// + +import Alamofire + +struct LoginResponse { + + let token: Token +} + +extension LoginResponse: EmptyResponse { + + static func emptyValue() -> LoginResponse { + LoginResponse(token: Token.defaultValue) + } +} + +extension LoginResponse: Decodable { + + enum CodingKeys: CodingKey { + case token + } + + init(from decoder: any Decoder) throws { + let singleContainer = try decoder.singleValueContainer() + self.token = try singleContainer.decode(Token.self) + } +} diff --git a/SOOUM/SOOUM/Data/Models/Responses/NicknameResponse.swift b/SOOUM/SOOUM/Data/Models/Responses/NicknameResponse.swift new file mode 100644 index 00000000..942bbc7e --- /dev/null +++ b/SOOUM/SOOUM/Data/Models/Responses/NicknameResponse.swift @@ -0,0 +1,33 @@ +// +// NicknameResponse.swift +// SOOUM +// +// Created by 오현식 on 9/18/25. +// + +import Alamofire + +struct NicknameResponse { + + let nickname: String +} + +extension NicknameResponse: EmptyResponse { + + static func emptyValue() -> NicknameResponse { + NicknameResponse(nickname: "") + } +} + +extension NicknameResponse: Decodable { + + enum CodingKeys: String, CodingKey { + case nickname + } + + init(from decoder: any Decoder) throws { + let container = try decoder.container(keyedBy: CodingKeys.self) + self.nickname = try container.decode(String.self, forKey: .nickname) + } +} + diff --git a/SOOUM/SOOUM/Data/Models/Responses/NicknameValidateResponse.swift b/SOOUM/SOOUM/Data/Models/Responses/NicknameValidateResponse.swift new file mode 100644 index 00000000..980b99b2 --- /dev/null +++ b/SOOUM/SOOUM/Data/Models/Responses/NicknameValidateResponse.swift @@ -0,0 +1,20 @@ +// +// NicknameValidateResponse.swift +// SOOUM +// +// Created by 오현식 on 9/16/25. +// + +import Alamofire + +struct NicknameValidateResponse: Decodable { + + let isAvailable: Bool +} + +extension NicknameValidateResponse: EmptyResponse { + + static func emptyValue() -> NicknameValidateResponse { + NicknameValidateResponse(isAvailable: false) + } +} diff --git a/SOOUM/SOOUM/Data/Models/Responses/NotificationInfoResponse.swift b/SOOUM/SOOUM/Data/Models/Responses/NotificationInfoResponse.swift new file mode 100644 index 00000000..4737c103 --- /dev/null +++ b/SOOUM/SOOUM/Data/Models/Responses/NotificationInfoResponse.swift @@ -0,0 +1,44 @@ +// +// NotificationInfoResponse.swift +// SOOUM +// +// Created by 오현식 on 9/16/25. +// + +import Alamofire + +struct NotificationInfoResponse { + + let notificationInfo: CommonNotificationInfo + let targetCardId: String + let nickName: String +} + +extension NotificationInfoResponse: EmptyResponse { + + static func emptyValue() -> NotificationInfoResponse { + NotificationInfoResponse( + notificationInfo: CommonNotificationInfo.defaultValue, + targetCardId: "", + nickName: "" + ) + } +} + +extension NotificationInfoResponse: Decodable { + + enum CodingKeys: CodingKey { + case notificationInfo + case targetCardId + case nickName + } + + init(from decoder: any Decoder) throws { + let singleContainer = try decoder.singleValueContainer() + self.notificationInfo = try singleContainer.decode(CommonNotificationInfo.self) + + let container = try decoder.container(keyedBy: CodingKeys.self) + self.targetCardId = String(try container.decode(Int.self, forKey: .targetCardId)) + self.nickName = try container.decode(String.self, forKey: .nickName) + } +} diff --git a/SOOUM/SOOUM/Data/Models/Responses/SignUpResponse.swift b/SOOUM/SOOUM/Data/Models/Responses/SignUpResponse.swift new file mode 100644 index 00000000..f1b2b45f --- /dev/null +++ b/SOOUM/SOOUM/Data/Models/Responses/SignUpResponse.swift @@ -0,0 +1,28 @@ +// +// SignUpResponse.swift +// SOOUM +// +// Created by 오현식 on 9/16/25. +// + +import Alamofire + +struct SignUpResponse { + + let token: Token +} + +extension SignUpResponse: EmptyResponse { + + static func emptyValue() -> SignUpResponse { + SignUpResponse(token: Token.defaultValue) + } +} + +extension SignUpResponse: Decodable { + + init(from decoder: any Decoder) throws { + let singleContainer = try decoder.singleValueContainer() + self.token = try singleContainer.decode(Token.self) + } +} diff --git a/SOOUM/SOOUM/Data/Models/Responses/ToeknResponse.swift b/SOOUM/SOOUM/Data/Models/Responses/ToeknResponse.swift new file mode 100644 index 00000000..7303eb2d --- /dev/null +++ b/SOOUM/SOOUM/Data/Models/Responses/ToeknResponse.swift @@ -0,0 +1,20 @@ +// +// TokenResponse.swift +// SOOUM +// +// Created by 오현식 on 9/17/25. +// + +import Alamofire + +struct TokenResponse: Decodable { + + let token: Token +} + +extension TokenResponse: EmptyResponse { + + static func emptyValue() -> TokenResponse { + TokenResponse(token: Token.defaultValue) + } +} diff --git a/SOOUM/SOOUM/Data/Repositories/AppVersionRepositoryImpl.swift b/SOOUM/SOOUM/Data/Repositories/AppVersionRepositoryImpl.swift new file mode 100644 index 00000000..683f5740 --- /dev/null +++ b/SOOUM/SOOUM/Data/Repositories/AppVersionRepositoryImpl.swift @@ -0,0 +1,29 @@ +// +// AppVersionRepositoryImpl.swift +// SOOUM +// +// Created by 오현식 on 9/16/25. +// + +import Foundation + +import RxSwift + +class AppVersionRepositoryImpl: AppVersionRepository { + + private let remoteDataSource: AppVersionRemoteDataSource + + init(remoteDataSource: AppVersionRemoteDataSource) { + self.remoteDataSource = remoteDataSource + } + + func version() -> Observable { + + return self.remoteDataSource.version() + } + + func oldVersion() -> Observable { + + return self.remoteDataSource.oldVersion() + } +} diff --git a/SOOUM/SOOUM/Data/Repositories/AuthRepositoryImpl.swift b/SOOUM/SOOUM/Data/Repositories/AuthRepositoryImpl.swift new file mode 100644 index 00000000..415c5d6b --- /dev/null +++ b/SOOUM/SOOUM/Data/Repositories/AuthRepositoryImpl.swift @@ -0,0 +1,46 @@ +// +// AuthRepositoryImpl.swift +// SOOUM +// +// Created by 오현식 on 9/17/25. +// + +import Foundation + +import RxSwift + +class AuthRepositoryImpl: AuthRepository { + + private let remoteDataSource: AuthRemoteDataSource + private let localDataSource: AuthLocalDataSource + + init(remoteDataSource: AuthRemoteDataSource, localDataSource: AuthLocalDataSource) { + self.remoteDataSource = remoteDataSource + self.localDataSource = localDataSource + } + + func signUp(nickname: String, profileImageName: String?) -> Observable { + + self.remoteDataSource.signUp(nickname: nickname, profileImageName: profileImageName) + } + + func login() -> Observable { + + self.remoteDataSource.login() + } + + func initializeAuthInfo() { + + self.localDataSource.initializeAuthInfo() + } + + func hasToken() -> Bool { + + self.localDataSource.hasToken() + } + + func tokens() -> Token { + + self.localDataSource.tokens() + } +} diff --git a/SOOUM/SOOUM/Data/Repositories/Locals/AuthLocalDataSourceImpl.swift b/SOOUM/SOOUM/Data/Repositories/Locals/AuthLocalDataSourceImpl.swift new file mode 100644 index 00000000..ac6238fd --- /dev/null +++ b/SOOUM/SOOUM/Data/Repositories/Locals/AuthLocalDataSourceImpl.swift @@ -0,0 +1,35 @@ +// +// AuthLocalDataSourceImpl.swift +// SOOUM +// +// Created by 오현식 on 9/17/25. +// + +import Foundation + +import RxSwift + +class AuthLocalDataSourceImpl: AuthLocalDataSource { + + private let provider: ManagerProviderType + + init(provider: ManagerProviderType) { + self.provider = provider + } + + func initializeAuthInfo() { + + self.provider.authManager.initializeAuthInfo() + } + + func hasToken() -> Bool { + + let authInfo = self.provider.authManager.authInfo + return authInfo.token.accessToken.isEmpty == false && authInfo.token.refreshToken.isEmpty == false + } + + func tokens() -> Token { + + return self.provider.authManager.authInfo.token + } +} diff --git a/SOOUM/SOOUM/Data/Repositories/Locals/Interfaces/AuthLocalDataSource.swift b/SOOUM/SOOUM/Data/Repositories/Locals/Interfaces/AuthLocalDataSource.swift new file mode 100644 index 00000000..5d8b9895 --- /dev/null +++ b/SOOUM/SOOUM/Data/Repositories/Locals/Interfaces/AuthLocalDataSource.swift @@ -0,0 +1,15 @@ +// +// AuthLocalDataSource.swift +// SOOUM +// +// Created by 오현식 on 9/17/25. +// + +import Foundation + +protocol AuthLocalDataSource { + + func initializeAuthInfo() + func hasToken() -> Bool + func tokens() -> Token +} diff --git a/SOOUM/SOOUM/Data/Repositories/NotificationRepositoryImpl.swift b/SOOUM/SOOUM/Data/Repositories/NotificationRepositoryImpl.swift new file mode 100644 index 00000000..ba93e710 --- /dev/null +++ b/SOOUM/SOOUM/Data/Repositories/NotificationRepositoryImpl.swift @@ -0,0 +1,64 @@ +// +// NotificationRepositoryImpl.swift +// SOOUM +// +// Created by 오현식 on 9/17/25. +// + +import Foundation + +import RxSwift + +class NotificationRepositoryImpl: NotificationRepository { + + private let remoteDataSource: NotificationRemoteDataSource + + init(remoteDataSource: NotificationRemoteDataSource) { + self.remoteDataSource = remoteDataSource + } + + func unreadNotifications(lastId: String?) -> Observable<[NotificationInfoResponse]> { + + return self.remoteDataSource.unreadNotifications(lastId: lastId) + } + + func unreadCardNotifications(lastId: String?) -> Observable<[NotificationInfoResponse]> { + + return self.remoteDataSource.unreadCardNotifications(lastId: lastId) + } + + func unreadFollowNotifications(lastId: String?) -> Observable<[NotificationInfoResponse]> { + + return self.remoteDataSource.unreadFollowNotifications(lastId: lastId) + } + + func unreadNoticeNoticeNotifications(lastId: String?) -> Observable<[NotificationInfoResponse]> { + + return self.remoteDataSource.unreadNoticeNoticeNotifications(lastId: lastId) + } + + func readNotifications(lastId: String?) -> Observable<[NotificationInfoResponse]> { + + return self.remoteDataSource.readNotifications(lastId: lastId) + } + + func readCardNotifications(lastId: String?) -> Observable<[NotificationInfoResponse]> { + + return self.remoteDataSource.readCardNotifications(lastId: lastId) + } + + func readFollowNotifications(lastId: String?) -> Observable<[NotificationInfoResponse]> { + + return self.remoteDataSource.readFollowNotifications(lastId: lastId) + } + + func readNoticeNoticeNotifications(lastId: String?) -> Observable<[NotificationInfoResponse]> { + + return self.remoteDataSource.readNoticeNoticeNotifications(lastId: lastId) + } + + func requestRead(notificationId: String) -> Observable { + + return self.remoteDataSource.requestRead(notificationId: notificationId) + } +} diff --git a/SOOUM/SOOUM/Data/Repositories/Remotes/AppVersionRemoteDataSourceImpl.swift b/SOOUM/SOOUM/Data/Repositories/Remotes/AppVersionRemoteDataSourceImpl.swift new file mode 100644 index 00000000..355cc68d --- /dev/null +++ b/SOOUM/SOOUM/Data/Repositories/Remotes/AppVersionRemoteDataSourceImpl.swift @@ -0,0 +1,29 @@ +// +// AppVersionRemoteDataSourceImpl.swift +// SOOUM +// +// Created by 오현식 on 9/16/25. +// + +import Foundation + +import RxSwift + +class AppVersionRemoteDataSourceImpl: AppVersionRemoteDataSource { + + private let provider: ManagerProviderType + + init(provider: ManagerProviderType) { + self.provider = provider + } + + func version() -> Observable { + + return self.provider.networkManager.updateCheck() + } + + func oldVersion() -> Observable { + + return self.provider.networkManager.fetch(String.self, request: VersionRequest.version) + } +} diff --git a/SOOUM/SOOUM/Data/Repositories/Remotes/AuthRemoteDataSourceImpl.swift b/SOOUM/SOOUM/Data/Repositories/Remotes/AuthRemoteDataSourceImpl.swift new file mode 100644 index 00000000..87de69e8 --- /dev/null +++ b/SOOUM/SOOUM/Data/Repositories/Remotes/AuthRemoteDataSourceImpl.swift @@ -0,0 +1,29 @@ +// +// AuthRemoteDataSourceImpl.swift +// SOOUM +// +// Created by 오현식 on 9/17/25. +// + +import Foundation + +import RxSwift + +class AuthRemoteDataSourceImpl: AuthRemoteDataSource { + + private let provider: ManagerProviderType + + init(provider: ManagerProviderType) { + self.provider = provider + } + + func signUp(nickname: String, profileImageName: String?) -> Observable { + + return self.provider.authManager.join(nickname: nickname, profileImageName: profileImageName) + } + + func login() -> Observable { + + return self.provider.authManager.certification() + } +} diff --git a/SOOUM/SOOUM/Data/Repositories/Remotes/Interfaces/AppVersionRemoteDataSource.swift b/SOOUM/SOOUM/Data/Repositories/Remotes/Interfaces/AppVersionRemoteDataSource.swift new file mode 100644 index 00000000..2b41c017 --- /dev/null +++ b/SOOUM/SOOUM/Data/Repositories/Remotes/Interfaces/AppVersionRemoteDataSource.swift @@ -0,0 +1,16 @@ +// +// AppVersionRemoteDataSource.swift +// SOOUM +// +// Created by 오현식 on 9/16/25. +// + +import Foundation + +import RxSwift + +protocol AppVersionRemoteDataSource { + + func version() -> Observable + func oldVersion() -> Observable +} diff --git a/SOOUM/SOOUM/Data/Repositories/Remotes/Interfaces/AuthRemoteDataSource.swift b/SOOUM/SOOUM/Data/Repositories/Remotes/Interfaces/AuthRemoteDataSource.swift new file mode 100644 index 00000000..32cbd775 --- /dev/null +++ b/SOOUM/SOOUM/Data/Repositories/Remotes/Interfaces/AuthRemoteDataSource.swift @@ -0,0 +1,16 @@ +// +// AuthRemoteDataSource.swift +// SOOUM +// +// Created by 오현식 on 9/17/25. +// + +import Foundation + +import RxSwift + +protocol AuthRemoteDataSource { + + func signUp(nickname: String, profileImageName: String?) -> Observable + func login() -> Observable +} diff --git a/SOOUM/SOOUM/Data/Repositories/Remotes/Interfaces/NotificationRemoteDataSource.swift b/SOOUM/SOOUM/Data/Repositories/Remotes/Interfaces/NotificationRemoteDataSource.swift new file mode 100644 index 00000000..73959f9c --- /dev/null +++ b/SOOUM/SOOUM/Data/Repositories/Remotes/Interfaces/NotificationRemoteDataSource.swift @@ -0,0 +1,23 @@ +// +// NotificationRemoteDataSource.swift +// SOOUM +// +// Created by 오현식 on 9/17/25. +// + +import Foundation + +import RxSwift + +protocol NotificationRemoteDataSource { + + func unreadNotifications(lastId: String?) -> Observable<[NotificationInfoResponse]> + func unreadCardNotifications(lastId: String?) -> Observable<[NotificationInfoResponse]> + func unreadFollowNotifications(lastId: String?) -> Observable<[NotificationInfoResponse]> + func unreadNoticeNoticeNotifications(lastId: String?) -> Observable<[NotificationInfoResponse]> + func readNotifications(lastId: String?) -> Observable<[NotificationInfoResponse]> + func readCardNotifications(lastId: String?) -> Observable<[NotificationInfoResponse]> + func readFollowNotifications(lastId: String?) -> Observable<[NotificationInfoResponse]> + func readNoticeNoticeNotifications(lastId: String?) -> Observable<[NotificationInfoResponse]> + func requestRead(notificationId: String) -> Observable +} diff --git a/SOOUM/SOOUM/Data/Repositories/Remotes/Interfaces/UserRemoteDataSource.swift b/SOOUM/SOOUM/Data/Repositories/Remotes/Interfaces/UserRemoteDataSource.swift new file mode 100644 index 00000000..09128c4e --- /dev/null +++ b/SOOUM/SOOUM/Data/Repositories/Remotes/Interfaces/UserRemoteDataSource.swift @@ -0,0 +1,22 @@ +// +// UserRemoteDataSource.swift +// SOOUM +// +// Created by 오현식 on 9/17/25. +// + +import Foundation + +import RxSwift + +protocol UserRemoteDataSource { + + func checkAvailable() -> Observable + func nickname() -> Observable + func validateNickname(nickname: String) -> Observable + func updateNickname(nickname: String) -> Observable + func presignedURL() -> Observable + func uploadImage(_ data: Data, with url: URL) -> Observable> + func updateImage(imageName: String) -> Observable + func updateFCMToken(fcmToken: String) -> Observable +} diff --git a/SOOUM/SOOUM/Data/Repositories/Remotes/NotificationRemoteDataSoruceImpl.swift b/SOOUM/SOOUM/Data/Repositories/Remotes/NotificationRemoteDataSoruceImpl.swift new file mode 100644 index 00000000..b509768c --- /dev/null +++ b/SOOUM/SOOUM/Data/Repositories/Remotes/NotificationRemoteDataSoruceImpl.swift @@ -0,0 +1,73 @@ +// +// NotificationRemoteDataSoruceImpl.swift +// SOOUM +// +// Created by 오현식 on 9/17/25. +// + +import Foundation + +import RxSwift + +class NotificationRemoteDataSoruceImpl: NotificationRemoteDataSource { + + private let provider: ManagerProviderType + + init(provider: ManagerProviderType) { + self.provider = provider + } + + func unreadNotifications(lastId: String?) -> Observable<[NotificationInfoResponse]> { + + let request: NotificationRequest = .unreadNotifications(lastId: lastId) + return self.provider.networkManager.fetch([NotificationInfoResponse].self, request: request) + } + + func unreadCardNotifications(lastId: String?) -> Observable<[NotificationInfoResponse]> { + + let request: NotificationRequest = .unreadCardNotifications(lastId: lastId) + return self.provider.networkManager.fetch([NotificationInfoResponse].self, request: request) + } + + func unreadFollowNotifications(lastId: String?) -> Observable<[NotificationInfoResponse]> { + + let request: NotificationRequest = .unreadFollowNotifications(lastId: lastId) + return self.provider.networkManager.fetch([NotificationInfoResponse].self, request: request) + } + + func unreadNoticeNoticeNotifications(lastId: String?) -> Observable<[NotificationInfoResponse]> { + + let request: NotificationRequest = .unreadNoticeNoticeNotifications(lastId: lastId) + return self.provider.networkManager.fetch([NotificationInfoResponse].self, request: request) + } + + func readNotifications(lastId: String?) -> Observable<[NotificationInfoResponse]> { + + let request: NotificationRequest = .readNotifications(lastId: lastId) + return self.provider.networkManager.fetch([NotificationInfoResponse].self, request: request) + } + + func readCardNotifications(lastId: String?) -> Observable<[NotificationInfoResponse]> { + + let request: NotificationRequest = .readCardNotifications(lastId: lastId) + return self.provider.networkManager.fetch([NotificationInfoResponse].self, request: request) + } + + func readFollowNotifications(lastId: String?) -> Observable<[NotificationInfoResponse]> { + + let request: NotificationRequest = .readFollowNotifications(lastId: lastId) + return self.provider.networkManager.fetch([NotificationInfoResponse].self, request: request) + } + + func readNoticeNoticeNotifications(lastId: String?) -> Observable<[NotificationInfoResponse]> { + + let request: NotificationRequest = .readNoticeNoticeNotifications(lastId: lastId) + return self.provider.networkManager.fetch([NotificationInfoResponse].self, request: request) + } + + func requestRead(notificationId: String) -> Observable { + + let request: NotificationRequest = .requestRead(notificationId: notificationId) + return self.provider.networkManager.perform(Int.self, request: request) + } +} diff --git a/SOOUM/SOOUM/Data/Repositories/Remotes/UserRemoteDataSourceImpl.swift b/SOOUM/SOOUM/Data/Repositories/Remotes/UserRemoteDataSourceImpl.swift new file mode 100644 index 00000000..59a8215c --- /dev/null +++ b/SOOUM/SOOUM/Data/Repositories/Remotes/UserRemoteDataSourceImpl.swift @@ -0,0 +1,65 @@ +// +// UserRemoteDataSourceImpl.swift +// SOOUM +// +// Created by 오현식 on 9/17/25. +// + +import Foundation + +import RxSwift + +class UserRemoteDataSourceImpl: UserRemoteDataSource { + + private let provider: ManagerProviderType + + init(provider: ManagerProviderType) { + self.provider = provider + } + + func checkAvailable() -> Observable { + + return self.provider.authManager.available() + } + + func nickname() -> Observable { + + let request: UserRequest = .nickname + return self.provider.networkManager.fetch(NicknameResponse.self, request: request) + } + + func validateNickname(nickname: String) -> Observable { + + let request: UserRequest = .validateNickname(nickname: nickname) + return self.provider.networkManager.perform(NicknameValidateResponse.self, request: request) + } + + func updateNickname(nickname: String) -> Observable { + + let request: UserRequest = .updateNickname(nickname: nickname) + return self.provider.networkManager.perform(Int.self, request: request) + } + + func presignedURL() -> Observable { + + let request: UserRequest = .presignedURL + return self.provider.networkManager.perform(ImageUrlInfoResponse.self, request: request) + } + + func uploadImage(_ data: Data, with url: URL) -> Observable> { + + return self.provider.networkManager.upload(data, to: url) + } + + func updateImage(imageName: String) -> Observable { + + let request: UserRequest = .updateImage(imageName: imageName) + return self.provider.networkManager.perform(Int.self, request: request) + } + + func updateFCMToken(fcmToken: String) -> Observable { + + let request: UserRequest = .updateFCMToken(fcmToken: fcmToken) + return self.provider.networkManager.perform(Int.self, request: request) + } +} diff --git a/SOOUM/SOOUM/Data/Repositories/UserRepositoryImpl.swift b/SOOUM/SOOUM/Data/Repositories/UserRepositoryImpl.swift new file mode 100644 index 00000000..a8e50e37 --- /dev/null +++ b/SOOUM/SOOUM/Data/Repositories/UserRepositoryImpl.swift @@ -0,0 +1,59 @@ +// +// UserRepositoryImpl.swift +// SOOUM +// +// Created by 오현식 on 9/17/25. +// + +import Foundation + +import RxSwift + +class UserRepositoryImpl: UserRepository { + + private let remoteDataSource: UserRemoteDataSource + + init(remoteDataSource: UserRemoteDataSource) { + self.remoteDataSource = remoteDataSource + } + + func checkAvailable() -> Observable { + + return self.remoteDataSource.checkAvailable() + } + + func nickname() -> Observable { + + return self.remoteDataSource.nickname() + } + + func validateNickname(nickname: String) -> Observable { + + return self.remoteDataSource.validateNickname(nickname: nickname) + } + + func updateNickname(nickname: String) -> Observable { + + return self.remoteDataSource.updateNickname(nickname: nickname) + } + + func presignedURL() -> Observable { + + return self.remoteDataSource.presignedURL() + } + + func uploadImage(_ data: Data, with url: URL) -> Observable> { + + return self.remoteDataSource.uploadImage(data, with: url) + } + + func updateImage(imageName: String) -> Observable { + + return self.remoteDataSource.updateImage(imageName: imageName) + } + + func updateFCMToken(fcmToken: String) -> Observable { + + return self.remoteDataSource.updateFCMToken(fcmToken: fcmToken) + } +} diff --git a/SOOUM/SOOUM/DesignSystem/Components/SOMActivityIndicatorView.swift b/SOOUM/SOOUM/DesignSystem/Components/SOMActivityIndicatorView.swift index 75bb28f6..21507717 100644 --- a/SOOUM/SOOUM/DesignSystem/Components/SOMActivityIndicatorView.swift +++ b/SOOUM/SOOUM/DesignSystem/Components/SOMActivityIndicatorView.swift @@ -71,13 +71,13 @@ class SOMActivityIndicatorView: UIActivityIndicatorView { self.tintColor = .clear - self.addSubviews(self.backgroundView) + self.addSubview(self.backgroundView) self.backgroundView.snp.makeConstraints { $0.center.equalToSuperview() $0.size.equalTo(40) } - self.backgroundView.addSubviews(self.imageView) + self.backgroundView.addSubview(self.imageView) self.imageView.snp.makeConstraints { $0.center.equalToSuperview() $0.size.equalTo(28) diff --git a/SOOUM/SOOUM/DesignSystem/Components/SOMButton.swift b/SOOUM/SOOUM/DesignSystem/Components/SOMButton.swift index b65e5a05..44ac2d92 100644 --- a/SOOUM/SOOUM/DesignSystem/Components/SOMButton.swift +++ b/SOOUM/SOOUM/DesignSystem/Components/SOMButton.swift @@ -7,105 +7,158 @@ import UIKit - class SOMButton: UIButton { var title: String? { didSet { if oldValue != self.title { - self.setConfiguration() + self.setNeedsUpdateConfiguration() } } } - var image: UIImage? { + var typography: Typography? { didSet { - if oldValue != self.image { - self.setConfiguration() + if oldValue != self.typography { + self.setNeedsUpdateConfiguration() } } } - var typography: Typography? { + var hasUnderlined: Bool? { didSet { - if oldValue != self.typography { - self.setConfiguration() + if oldValue != self.hasUnderlined { + self.setNeedsUpdateConfiguration() } } } - var foregroundColor: UIColor? { + var inset: UIEdgeInsets? { didSet { - if oldValue != self.foregroundColor { - self.setConfiguration() + if oldValue != self.inset { + self.setNeedsUpdateConfiguration() } } } - var hasUnderlined: Bool? { + var image: UIImage? { didSet { - if oldValue != self.hasUnderlined { - self.setConfiguration() + if oldValue != self.image { + self.setNeedsUpdateConfiguration() + } + } + } + + var imagePlacement: NSDirectionalRectEdge? { + didSet { + if oldValue != self.imagePlacement { + self.setNeedsUpdateConfiguration() + } + } + } + + var foregroundColor: UIColor? { + didSet { + if oldValue != self.foregroundColor { + self.setNeedsUpdateConfiguration() } } } override init(frame: CGRect) { super.init(frame: .zero) + self.setupConfiguration() } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } +} + +private extension SOMButton { - private func setConfiguration() { + func setupConfiguration() { var configuration = UIButton.Configuration.plain() + configuration.contentInsets = .zero - // 이미지 설정 - if let image = self.image { - configuration.image = image + self.configuration = configuration + self.backgroundColor = .clear + self.layer.cornerRadius = 10 + self.clipsToBounds = true + + self.configurationUpdateHandler = { [weak self] button in + guard let self = self else { return } + + var updatedConfig = button.configuration - if let foregroundColor = self.foregroundColor { - configuration.image?.withTintColor(foregroundColor) - configuration.imageColorTransformer = UIConfigurationColorTransformer { _ in - foregroundColor + updatedConfig?.background.backgroundColor = self.backgroundColor + updatedConfig?.background.backgroundColorTransformer = UIConfigurationColorTransformer { _ in + // 비활성화 상태일 때, backgroundColor + if button.isEnabled == false { + return .som.v2.gray200 } + // 하이라이트 상태일 때, backgroundColor + if button.isHighlighted { + var highlightedColor: UIColor { + if self.backgroundColor == .som.v2.black { + return .som.v2.gray600 + } + if self.backgroundColor == .som.v2.gray100 { + return .som.v2.gray200 + } + if self.backgroundColor == .clear || self.backgroundColor == .som.v2.white { + return .som.v2.gray100 + } + return self.backgroundColor ?? .clear + } + return highlightedColor + } + // 기본 상태일 때, backgroundColor + return self.backgroundColor ?? .clear } + + self.applyConfiguration(to: &updatedConfig) + button.configuration = updatedConfig + } + } + + func applyConfiguration(to configuration: inout UIButton.Configuration?) { + + var foregroundColor: UIColor { + return self.isEnabled ? (self.foregroundColor ?? .som.v2.white) : .som.v2.gray400 + } + + if let image = self.image { + configuration?.image = image + configuration?.imageColorTransformer = UIConfigurationColorTransformer { _ in foregroundColor } + configuration?.imagePadding = 8 + configuration?.imagePlacement = self.imagePlacement ?? .leading } - // 타이틀 설정 if let title = self.title, let typography = self.typography { var attributes = typography.attributes attributes.updateValue(typography.font, forKey: .font) - - if let foregroundColor = self.foregroundColor { - attributes.updateValue(foregroundColor, forKey: .foregroundColor) - } + attributes.updateValue(foregroundColor, forKey: .foregroundColor) if self.hasUnderlined == true { attributes.updateValue(NSUnderlineStyle.single.rawValue, forKey: .underlineStyle) - attributes.updateValue(foregroundColor ?? UIColor.som.gray400, forKey: .underlineColor) + attributes.updateValue(foregroundColor, forKey: .underlineColor) } - configuration.attributedTitle = .init(title, attributes: AttributeContainer(attributes)) - configuration.titleTextAttributesTransformer = UIConfigurationTextAttributesTransformer { _ in - AttributeContainer(attributes) + if let inset = self.inset { + configuration?.contentInsets = .init( + top: inset.top, + leading: inset.left, + bottom: inset.bottom, + trailing: inset.right + ) } - if image == nil { - configuration.titleAlignment = .center - } else { - configuration.imagePadding = 2 + configuration?.attributedTitle = .init(title, attributes: AttributeContainer(attributes)) + configuration?.titleTextAttributesTransformer = UIConfigurationTextAttributesTransformer { _ in + AttributeContainer(attributes) } } - - if self.backgroundColor == nil { - self.backgroundColor = .clear - - configuration.contentInsets = .zero - } - - self.configuration = configuration } } diff --git a/SOOUM/SOOUM/DesignSystem/Components/SOMDialogController/SOMDialogAction.swift b/SOOUM/SOOUM/DesignSystem/Components/SOMDialogController/SOMDialogAction.swift index 632883f2..c5475e38 100644 --- a/SOOUM/SOOUM/DesignSystem/Components/SOMDialogController/SOMDialogAction.swift +++ b/SOOUM/SOOUM/DesignSystem/Components/SOMDialogController/SOMDialogAction.swift @@ -17,7 +17,7 @@ class SOMDialogAction { var backgroundColor: UIColor { switch self { case .primary: - return .som.p300 + return .som.v2.black case .gray: return .som.gray300 } @@ -26,7 +26,7 @@ class SOMDialogAction { var foregroundColor: UIColor { switch self { case .primary: - return .som.white + return .som.v2.white case .gray: return .som.gray700 } diff --git a/SOOUM/SOOUM/DesignSystem/Components/SOMDialogController/SOMDialogViewController+Show.swift b/SOOUM/SOOUM/DesignSystem/Components/SOMDialogController/SOMDialogViewController+Show.swift index 124969b1..c285d268 100644 --- a/SOOUM/SOOUM/DesignSystem/Components/SOMDialogController/SOMDialogViewController+Show.swift +++ b/SOOUM/SOOUM/DesignSystem/Components/SOMDialogController/SOMDialogViewController+Show.swift @@ -14,6 +14,7 @@ extension SOMDialogViewController { static func show( title: String, message: String, + textAlignment: NSTextAlignment = .center, actions: [SOMDialogAction], dismissesWhenBackgroundTouched: Bool = false, completion: ((SOMDialogViewController) -> Void)? = nil @@ -24,6 +25,7 @@ extension SOMDialogViewController { let dialogViewController = SOMDialogViewController( title: title, message: message, + textAlignment: textAlignment, completion: { alertController in window.windowScene = nil completion?(alertController) @@ -40,6 +42,7 @@ extension SOMDialogViewController { static func show( title: String, messageView: UIView, + textAlignment: NSTextAlignment = .center, actions: [SOMDialogAction], dismissesWhenBackgroundTouched: Bool = false, completion: ((SOMDialogViewController) -> Void)? = nil @@ -50,6 +53,7 @@ extension SOMDialogViewController { let dialogViewController = SOMDialogViewController( title: title, messageView: messageView, + textAlignment: textAlignment, completion: { alertController in window.windowScene = nil completion?(alertController) diff --git a/SOOUM/SOOUM/DesignSystem/Components/SOMDialogController/SOMDialogViewController.swift b/SOOUM/SOOUM/DesignSystem/Components/SOMDialogController/SOMDialogViewController.swift index 76fcd83d..551033e4 100644 --- a/SOOUM/SOOUM/DesignSystem/Components/SOMDialogController/SOMDialogViewController.swift +++ b/SOOUM/SOOUM/DesignSystem/Components/SOMDialogController/SOMDialogViewController.swift @@ -18,25 +18,25 @@ class SOMDialogViewController: UIViewController { /// container 밖의 영역 private let backgroundButton = UIButton().then { - $0.backgroundColor = .som.dim + $0.backgroundColor = .som.v2.dim } private let containerView = UIView().then { - $0.backgroundColor = .som.white + $0.backgroundColor = .som.v2.white $0.layer.cornerRadius = 20 } private let titleLabel = UILabel().then { - $0.textColor = .som.black - $0.typography = .som.body1WithBold + $0.textColor = .som.v2.black + $0.typography = .som.v2.head3 $0.lineBreakMode = .byWordWrapping $0.lineBreakStrategy = .hangulWordPriority $0.numberOfLines = 0 } private let messageLabel = UILabel().then { - $0.textColor = .som.gray600 - $0.typography = .som.body2WithRegular + $0.textColor = .som.v2.gray600 + $0.typography = .som.v2.body1 $0.lineBreakMode = .byWordWrapping $0.lineBreakStrategy = .hangulWordPriority $0.numberOfLines = 0 @@ -58,7 +58,7 @@ class SOMDialogViewController: UIViewController { private var message: String? { set { if let message = newValue { - let attributes = Typography.som.body2WithRegular.attributes + let attributes = Typography.som.v2.body1.attributes self.messageLabel.attributedText = .init(string: message, attributes: attributes) } self.messageLabel.isHidden = (newValue == nil) @@ -104,22 +104,31 @@ class SOMDialogViewController: UIViewController { convenience init( title: String, message: String, + textAlignment: NSTextAlignment = .center, completion: ((SOMDialogViewController) -> Void)? = nil ) { - self.init(title: title, messageView: nil, completion: completion) + self.init(title: title, messageView: nil, textAlignment: textAlignment, completion: completion) self.message = message + self.messageLabel.textAlignment = textAlignment } - init(title: String, messageView: UIView?, completion: ((SOMDialogViewController) -> Void)? = nil) { + init( + title: String, + messageView: UIView?, + textAlignment: NSTextAlignment = .center, + completion: ((SOMDialogViewController) -> Void)? = nil + ) { self.messageView = messageView super.init(nibName: nil, bundle: nil) self.setupConstraints() - let attributes = Typography.som.body1WithBold.attributes + let attributes = Typography.som.v2.head3.attributes self.titleLabel.attributedText = .init(string: title, attributes: attributes) + self.titleLabel.textAlignment = textAlignment + self.completion = completion } @@ -149,16 +158,15 @@ class SOMDialogViewController: UIViewController { self.view.addSubview(self.containerView) self.containerView.snp.makeConstraints { $0.centerY.equalToSuperview() - $0.leading.equalToSuperview().offset(32) - $0.trailing.equalToSuperview().offset(-32) + $0.leading.equalToSuperview().offset(52) + $0.trailing.equalToSuperview().offset(-52) } self.containerView.addSubview(self.titleLabel) self.titleLabel.snp.makeConstraints { - $0.top.equalToSuperview().offset(20) - $0.leading.greaterThanOrEqualToSuperview().offset(20) - $0.trailing.lessThanOrEqualToSuperview().offset(-20) - $0.centerX.equalToSuperview() + $0.top.equalToSuperview().offset(16) + $0.leading.equalToSuperview().offset(24) + $0.trailing.equalToSuperview().offset(-24) } if let messageView = self.messageView { @@ -166,28 +174,27 @@ class SOMDialogViewController: UIViewController { self.containerView.addSubview(messageView) messageView.snp.makeConstraints { $0.top.equalTo(self.titleLabel.snp.bottom).offset(20) - $0.leading.equalToSuperview().offset(20) - $0.trailing.equalToSuperview().offset(-20) + $0.leading.equalToSuperview().offset(24) + $0.trailing.equalToSuperview().offset(-24) } } else { self.containerView.addSubview(self.messageLabel) self.messageLabel.snp.makeConstraints { - $0.top.equalTo(self.titleLabel.snp.bottom).offset(12) - $0.leading.greaterThanOrEqualToSuperview().offset(20) - $0.trailing.lessThanOrEqualToSuperview().offset(-20) - $0.centerX.equalToSuperview() + $0.top.equalTo(self.titleLabel.snp.bottom).offset(6) + $0.leading.equalToSuperview().offset(24) + $0.trailing.equalToSuperview().offset(-24) } } self.containerView.addSubview(self.buttonContainer) self.buttonContainer.snp.makeConstraints { let hasMessage = self.message != nil - $0.top.equalTo((self.messageView ?? self.messageLabel).snp.bottom).offset(hasMessage ? 20 : 27) - $0.bottom.equalToSuperview().offset(-14) - $0.leading.equalToSuperview().offset(14) - $0.trailing.equalToSuperview().offset(-14) - $0.height.equalTo(46) + $0.top.equalTo((self.messageView ?? self.messageLabel).snp.bottom).offset(hasMessage ? 20 : 24) + $0.bottom.equalToSuperview().offset(-16) + $0.leading.equalToSuperview().offset(24) + $0.trailing.equalToSuperview().offset(-24) + $0.height.equalTo(48) } } @@ -200,11 +207,11 @@ class SOMDialogViewController: UIViewController { let button = SOMButton().then { $0.title = action.title - $0.typography = .som.body1WithBold + $0.typography = .som.v2.subtitle1 $0.foregroundColor = action.style.foregroundColor $0.backgroundColor = action.style.backgroundColor - $0.layer.cornerRadius = 12 + $0.layer.cornerRadius = 10 $0.clipsToBounds = true } button.tag = action.tag diff --git a/SOOUM/SOOUM/DesignSystem/Components/SOMLoadingIndicatorView.swift b/SOOUM/SOOUM/DesignSystem/Components/SOMLoadingIndicatorView.swift new file mode 100644 index 00000000..b303d1d0 --- /dev/null +++ b/SOOUM/SOOUM/DesignSystem/Components/SOMLoadingIndicatorView.swift @@ -0,0 +1,75 @@ +// +// SOMLoadingIndicatorWithLottie.swift +// SOOUM +// +// Created by 오현식 on 9/12/25. +// + +import UIKit + +import SnapKit +import Then + +import Lottie + +class SOMLoadingIndicatorView: UIView { + + + // MARK: Views + + private let backgroundView = UIView().then { + $0.backgroundColor = .som.v2.dim + } + + private let animationView = LottieAnimationView(name: "loading_indicator_lottie").then { + $0.contentMode = .scaleAspectFit + $0.loopMode = .loop + } + + + // MARK: Init + + convenience init() { + self.init(frame: .zero) + } + + override init(frame: CGRect) { + super.init(frame: frame) + self.setupConstraints() + } + + required init(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + + // MARK: Public func + + func startAnimating() { + self.isHidden = false + self.animationView.play() + } + + func stopAnimating() { + self.isHidden = true + self.animationView.stop() + } + + + // MARK: Private func + + private func setupConstraints() { + + self.isHidden = true + + self.addSubview(self.backgroundView) + self.backgroundView.snp.makeConstraints { + $0.edges.equalToSuperview() + } + + self.backgroundView.addSubviews(self.animationView) + self.animationView.snp.makeConstraints { + $0.center.equalToSuperview() + } + } +} diff --git a/SOOUM/SOOUM/DesignSystem/Components/SOMNavigationBar/SOMNavigationBar.swift b/SOOUM/SOOUM/DesignSystem/Components/SOMNavigationBar/SOMNavigationBar.swift index 947a2eb9..8dd36754 100644 --- a/SOOUM/SOOUM/DesignSystem/Components/SOMNavigationBar/SOMNavigationBar.swift +++ b/SOOUM/SOOUM/DesignSystem/Components/SOMNavigationBar/SOMNavigationBar.swift @@ -24,7 +24,7 @@ class SOMNavigationBar: UIView { } /// 네비게이션 바 높이 - static let height: CGFloat = 44 + static let height: CGFloat = 48 private let centerContainer = UIView() private let leftContainer = UIStackView().then { @@ -54,8 +54,8 @@ class SOMNavigationBar: UIView { /// 타이틀 (text == label / logo == image) let titleLabel = UILabel().then { - $0.textColor = .som.black - $0.typography = .som.body1WithBold + $0.textColor = .som.v2.black + $0.typography = .som.v2.title1 } var title: String? { set { self.titleLabel.text = newValue } @@ -72,26 +72,26 @@ class SOMNavigationBar: UIView { /// 네비게이션 바 뒤로가기 버튼 let backButton = SOMButton().then { - $0.image = .init(.icon(.outlined(.arrowBack))) - $0.foregroundColor = .som.black + $0.image = .init(.icon(.v2(.outlined(.left)))) + $0.foregroundColor = .som.v2.black } var hidesBackButton: Bool { set { self.backButton.isHidden = newValue } get { self.backButton.isHidden } } - var spacing: CGFloat = 20 { + var spacing: CGFloat = 12 { didSet { self.refreshConstraints() } } - var leftInset: CGFloat = 20 { + var leftInset: CGFloat = 16 { didSet { self.refreshConstraints() } } - var rightInset: CGFloat = 20 { + var rightInset: CGFloat = 16 { didSet { self.refreshConstraints() } diff --git a/SOOUM/SOOUM/DesignSystem/Components/SOMTags/SOMTag.swift b/SOOUM/SOOUM/DesignSystem/Components/SOMTags/SOMTag.swift index 3a38d615..14c01394 100644 --- a/SOOUM/SOOUM/DesignSystem/Components/SOMTags/SOMTag.swift +++ b/SOOUM/SOOUM/DesignSystem/Components/SOMTags/SOMTag.swift @@ -24,7 +24,7 @@ class SOMTag: UICollectionViewCell { weak var delegate: SOMTagDelegate? private lazy var removeButton = SOMButton().then { - $0.image = .init(.image(.cancelTag)) + $0.image = .init(.image(.defaultStyle(.cancelTag))) $0.layer.cornerRadius = 8 $0.clipsToBounds = true diff --git a/SOOUM/SOOUM/DesignSystem/Foundations/SooumStyle.swift b/SOOUM/SOOUM/DesignSystem/Foundations/Style/SooumStyle.swift similarity index 99% rename from SOOUM/SOOUM/DesignSystem/Foundations/SooumStyle.swift rename to SOOUM/SOOUM/DesignSystem/Foundations/Style/SooumStyle.swift index b2f20934..c6e0e9b3 100644 --- a/SOOUM/SOOUM/DesignSystem/Foundations/SooumStyle.swift +++ b/SOOUM/SOOUM/DesignSystem/Foundations/Style/SooumStyle.swift @@ -7,15 +7,16 @@ import Foundation + // MARK: Styles +public struct SOOUMStyle { } + public protocol SOOUMStyleCompatible { associatedtype SOOUMStyleBase static var som: SOOUMStyle.Type { get } } -public struct SOOUMStyle { } - public extension SOOUMStyleCompatible { static var som: SOOUMStyle.Type { SOOUMStyle.self } } diff --git a/SOOUM/SOOUM/DesignSystem/Foundations/Style/SooumStyle_V2.swift b/SOOUM/SOOUM/DesignSystem/Foundations/Style/SooumStyle_V2.swift new file mode 100644 index 00000000..3f8803a5 --- /dev/null +++ b/SOOUM/SOOUM/DesignSystem/Foundations/Style/SooumStyle_V2.swift @@ -0,0 +1,17 @@ +// +// SooumStyle_V2.swift +// SOOUM +// +// Created by 오현식 on 9/16/25. +// + +import Foundation + + +// MARK: V2 + +public struct V2Style { } + +public extension SOOUMStyle { + static var v2: V2Style.Type { V2Style.self } +} diff --git a/SOOUM/SOOUM/DesignSystem/Foundations/Typography+SOOUM.swift b/SOOUM/SOOUM/DesignSystem/Foundations/Typography+SOOUM.swift index 77dea676..2ce38f6b 100644 --- a/SOOUM/SOOUM/DesignSystem/Foundations/Typography+SOOUM.swift +++ b/SOOUM/SOOUM/DesignSystem/Foundations/Typography+SOOUM.swift @@ -75,6 +75,9 @@ fileprivate extension UIFont { extension Typography: SOOUMStyleCompatible { } extension SOOUMStyle where Base == Typography { + + // MARK: v1 + // Pretendard static var head1WithBold: Typography = .init( fontContainer: BuiltInFont(size: 22, weight: .semibold), @@ -151,3 +154,108 @@ extension SOOUMStyle where Base == Typography { letterSpacing: -0.004 ) } + +extension V2Style where Base == Typography { + + + // MARK: v2 + + // Pretandard + + /// Size: 28, Line height: 39 + /// + /// Weight: [Thin: 100, UltraLight: 200, Light: 300, Regular: 400, Medium: 500, SemiBold: 600, Bold: 700, Heavy: 800, Black: 900] + static var head1: Typography = .init( + fontContainer: BuiltInFont(size: 28, weight: .bold), + lineHeight: 39, + letterSpacing: -0.025 + ) + /// Size: 24, Line height: 34 + /// + /// Weight: [Thin: 100, UltraLight: 200, Light: 300, Regular: 400, Medium: 500, SemiBold: 600, Bold: 700, Heavy: 800, Black: 900] + static var head2: Typography = .init( + fontContainer: BuiltInFont(size: 24, weight: .bold), + lineHeight: 34, + letterSpacing: -0.025 + ) + /// Size: 20, Line height: 28 + /// + /// Weight: [Thin: 100, UltraLight: 200, Light: 300, Regular: 400, Medium: 500, SemiBold: 600, Bold: 700, Heavy: 800, Black: 900] + static var head3: Typography = .init( + fontContainer: BuiltInFont(size: 20, weight: .bold), + lineHeight: 28, + letterSpacing: -0.025 + ) + /// Size: 18, Line height: 27 + /// + /// Weight: [Thin: 100, UltraLight: 200, Light: 300, Regular: 400, Medium: 500, SemiBold: 600, Bold: 700, Heavy: 800, Black: 900] + static var title1: Typography = .init( + fontContainer: BuiltInFont(size: 18, weight: .semibold), + lineHeight: 27, + letterSpacing: -0.025 + ) + /// Size: 16, Line height: 24 + /// + /// Weight: [Thin: 100, UltraLight: 200, Light: 300, Regular: 400, Medium: 500, SemiBold: 600, Bold: 700, Heavy: 800, Black: 900] + static var title2: Typography = .init( + fontContainer: BuiltInFont(size: 16, weight: .semibold), + lineHeight: 24, + letterSpacing: -0.025 + ) + /// Size: 16, Line height: 24 + /// + /// Weight: [Thin: 100, UltraLight: 200, Light: 300, Regular: 400, Medium: 500, SemiBold: 600, Bold: 700, Heavy: 800, Black: 900] + static var subtitle1: Typography = .init( + fontContainer: BuiltInFont(size: 16, weight: .medium), + lineHeight: 24, + letterSpacing: -0.025 + ) + /// Size: 14, Line height: 21 + /// + /// Weight: [Thin: 100, UltraLight: 200, Light: 300, Regular: 400, Medium: 500, SemiBold: 600, Bold: 700, Heavy: 800, Black: 900] + static var subtitle2: Typography = .init( + fontContainer: BuiltInFont(size: 14, weight: .semibold), + lineHeight: 21, + letterSpacing: -0.025 + ) + /// Size: 14, Line height: 21 + /// + /// Weight: [Thin: 100, UltraLight: 200, Light: 300, Regular: 400, Medium: 500, SemiBold: 600, Bold: 700, Heavy: 800, Black: 900] + static var body1: Typography = .init( + fontContainer: BuiltInFont(size: 14, weight: .medium), + lineHeight: 21, + letterSpacing: -0.025 + ) + /// Size: 14, Line height: 21 + /// + /// Weight: [Thin: 100, UltraLight: 200, Light: 300, Regular: 400, Medium: 500, SemiBold: 600, Bold: 700, Heavy: 800, Black: 900] + static var body2: Typography = .init( + fontContainer: BuiltInFont(size: 14, weight: .regular), + lineHeight: 21, + letterSpacing: -0.025 + ) + /// Size: 12, Line height: 18 + /// + /// Weight: [Thin: 100, UltraLight: 200, Light: 300, Regular: 400, Medium: 500, SemiBold: 600, Bold: 700, Heavy: 800, Black: 900] + static var caption1: Typography = .init( + fontContainer: BuiltInFont(size: 12, weight: .semibold), + lineHeight: 18, + letterSpacing: -0.025 + ) + /// Size: 12, Line height: 18 + /// + /// Weight: [Thin: 100, UltraLight: 200, Light: 300, Regular: 400, Medium: 500, SemiBold: 600, Bold: 700, Heavy: 800, Black: 900] + static var caption2: Typography = .init( + fontContainer: BuiltInFont(size: 12, weight: .medium), + lineHeight: 18, + letterSpacing: -0.025 + ) + /// Size: 10, Line height: 15 + /// + /// Weight: [Thin: 100, UltraLight: 200, Light: 300, Regular: 400, Medium: 500, SemiBold: 600, Bold: 700, Heavy: 800, Black: 900] + static var caption3: Typography = .init( + fontContainer: BuiltInFont(size: 10, weight: .medium), + lineHeight: 15, + letterSpacing: -0.025 + ) +} diff --git a/SOOUM/SOOUM/DesignSystem/Foundations/UIColor+SOOUM.swift b/SOOUM/SOOUM/DesignSystem/Foundations/UIColor+SOOUM.swift index 628e786c..11e83082 100644 --- a/SOOUM/SOOUM/DesignSystem/Foundations/UIColor+SOOUM.swift +++ b/SOOUM/SOOUM/DesignSystem/Foundations/UIColor+SOOUM.swift @@ -47,3 +47,37 @@ extension SOOUMStyle where Base == UIColor { // Dim static let dim = UIColor(red: 0, green: 0, blue: 0, alpha: 0.5) } + +extension V2Style where Base == UIColor { + + // Gray Scale + static let white = UIColor(hex: "#FFFFFF") + static let gray100 = UIColor(hex: "#F5F7FA") + static let gray200 = UIColor(hex: "#E4EAF1") + static let gray300 = UIColor(hex: "#BFC9D3") + static let gray400 = UIColor(hex: "#919DA9") + static let gray500 = UIColor(hex: "#5D6369") + static let gray600 = UIColor(hex: "#3A3F44") + static let black = UIColor(hex: "#212121") + + // Primary + static let pLight1 = UIColor(hex: "#D7F1F9") + static let pLight2 = UIColor(hex: "#8CE1F4") + static let pMain = UIColor(hex: "#20C6EC") + static let pDark = UIColor(hex: "#07ABD0") + + // Success + static let gLight = UIColor(hex: "#D3F5EB") + static let gMain = UIColor(hex: "#009262") + + // Warning + static let yLight = UIColor(hex: "#FFF0D7") + static let yMain = UIColor(hex: "#FFB240") + + // Danger + static let rLight = UIColor(hex: "#FFE1DF") + static let rMain = UIColor(hex: "#EE3A26") + + // Dim + static let dim = UIColor(r: 0, g: 0, b: 0, a: 0.6) +} diff --git a/SOOUM/SOOUM/DesignSystem/Foundations/UIImage+SOOUM.swift b/SOOUM/SOOUM/DesignSystem/Foundations/UIImage+SOOUM.swift index 93f6b851..0620392e 100644 --- a/SOOUM/SOOUM/DesignSystem/Foundations/UIImage+SOOUM.swift +++ b/SOOUM/SOOUM/DesignSystem/Foundations/UIImage+SOOUM.swift @@ -12,11 +12,12 @@ extension UIImage { enum SOOUMType: Equatable { case icon(IconStyle) case image(ImageStyle) - case logo + case logo(LogoStyle) enum IconStyle { case filled(Filled) case outlined(Outlined) + case v2(V2IconStyle) enum Filled: String { case addCard @@ -64,29 +65,59 @@ extension UIImage { var imageName: String { switch self { - case .filled(let filled): + case let .filled(filled): return "\(filled.rawValue)_filled" - case .outlined(let outlined): + case let .outlined(outlined): return "\(outlined.rawValue)_outlined" + case let .v2(iconStyle): + return "v2_\(iconStyle.imageName)" } } } - enum ImageStyle: String { - case cancelTag - case errorTriangle - case login - case sooumLogo + enum ImageStyle { + case defaultStyle(DefaultStyle) + case v2(V2ImageStyle) + + enum DefaultStyle: String { + case cancelTag + case errorTriangle + case login + case sooumLogo + } + + var imageName: String { + switch self { + case let .defaultStyle(defaultStyle): + return defaultStyle.rawValue + case let .v2(imageStyle): + return "v2_\(imageStyle.rawValue)" + } + } + } + + enum LogoStyle { + case logo + case v2(V2LogoStyle) + + var imageName: String { + switch self { + case .logo: + return "logo" + case let .v2(logoStyle): + return "v2_\(logoStyle.rawValue)" + } + } } var imageName: String { switch self { - case .icon(let iconStyle): + case let .icon(iconStyle): return iconStyle.imageName - case .image(let imageStyle): - return imageStyle.rawValue - case .logo: - return "logo" + case let .image(imageStyle): + return imageStyle.imageName + case let .logo(logoStyle): + return logoStyle.imageName } } @@ -99,3 +130,84 @@ extension UIImage { self.init(named: som.imageName) } } + + +// MARK: V2 + +extension UIImage.SOOUMType { + + enum V2IconStyle { + case filled(Filled) + case outlined(Outlined) + + enum Filled: String { + case bell + case camera + case danger + case heart + case home + case image + case info + case location + case message_circle + case message_square + case settings + case star + case tag + case time + case trash + case user + case write + } + + enum Outlined: String { + case bell + case camera + case check + case danger + case delete + case down + case error + case heart + case home + case image + case left + case location + case message_circle + case message_square + case more + case plus + case right + case search + case settings + case star + case swap + case tag + case time + case trash + case up + case user + case write + } + + var imageName: String { + switch self { + case let .filled(filled): + return "\(filled.rawValue)_filled" + case let .outlined(outlined): + return "\(outlined.rawValue)_outlined" + } + } + } + + enum V2ImageStyle: String { + case onboarding + case onboarding_finish + case check_square_light + case profile + } + + enum V2LogoStyle: String { + case logo_white + } +} diff --git a/SOOUM/SOOUM/Domain/Models/CheckAvailable.swift b/SOOUM/SOOUM/Domain/Models/CheckAvailable.swift new file mode 100644 index 00000000..2776f6fd --- /dev/null +++ b/SOOUM/SOOUM/Domain/Models/CheckAvailable.swift @@ -0,0 +1,44 @@ +// +// CheckAvailable.swift +// SOOUM +// +// Created by 오현식 on 9/16/25. +// + +import Foundation + +struct CheckAvailable: Equatable { + + let rejoinAvailableAt: Date? + let banned: Bool + let withdrawn: Bool + let registered: Bool + + enum CodingKeys: String, CodingKey { + case rejoinAvailableAt + case banned + case withdrawn + case registered + } +} + +extension CheckAvailable { + + static var defaultValue: CheckAvailable = CheckAvailable( + rejoinAvailableAt: nil, + banned: false, + withdrawn: false, + registered: false + ) +} + +extension CheckAvailable: Decodable { + + init(from decoder: any Decoder) throws { + let container = try decoder.container(keyedBy: CodingKeys.self) + self.rejoinAvailableAt = try container.decodeIfPresent(Date.self, forKey: .rejoinAvailableAt) + self.banned = try container.decode(Bool.self, forKey: .banned) + self.withdrawn = try container.decode(Bool.self, forKey: .withdrawn) + self.registered = try container.decode(Bool.self, forKey: .registered) + } +} diff --git a/SOOUM/SOOUM/Domain/Models/CommonNotificationInfo.swift b/SOOUM/SOOUM/Domain/Models/CommonNotificationInfo.swift new file mode 100644 index 00000000..56b71de1 --- /dev/null +++ b/SOOUM/SOOUM/Domain/Models/CommonNotificationInfo.swift @@ -0,0 +1,57 @@ +// +// NotificationInfo.swift +// SOOUM +// +// Created by 오현식 on 9/16/25. +// + +import Foundation + +struct CommonNotificationInfo: Equatable { + + let notificationId: String + let notificationType: NotificationType + let createTime: Date + + enum CodingKeys: CodingKey { + case notificationId + case notificationType + case createTime + } +} + +extension CommonNotificationInfo { + + static var defaultValue: CommonNotificationInfo = CommonNotificationInfo( + notificationId: "", + notificationType: .none, + createTime: Date() + ) +} + +extension CommonNotificationInfo { + + enum NotificationType: String { + + case feedLike = "FEED_LIKE" + case commentLike = "COMMENT_LIKE" + case commentWrite = "COMMENT_WRITE" + case blocked = "BLOCKED" + case deleted = "DELETED" + case transferSuccess = "TRANSFER_SUCCESS" + case follow = "FOLLOW" + case notice = "NOTICE" + case none = "NONE" + } +} + +extension CommonNotificationInfo.NotificationType: Decodable { } +extension CommonNotificationInfo: Decodable { + + init(from decoder: any Decoder) throws { + let container = try decoder.container(keyedBy: CodingKeys.self) + self.notificationId = String(try container.decode(Int.self, forKey: .notificationId)) + self.notificationType = try container.decode(CommonNotificationInfo.NotificationType.self, forKey: .notificationType) + self.createTime = try container.decode(Date.self, forKey: .createTime) + } +} diff --git a/SOOUM/SOOUM/Domain/Models/ImageUrlInfo.swift b/SOOUM/SOOUM/Domain/Models/ImageUrlInfo.swift new file mode 100644 index 00000000..1a05392d --- /dev/null +++ b/SOOUM/SOOUM/Domain/Models/ImageUrlInfo.swift @@ -0,0 +1,21 @@ +// +// ImageUrlInfo.swift +// SOOUM +// +// Created by 오현식 on 9/16/25. +// + +import Foundation + +struct ImageUrlInfo: Equatable { + + let imgName: String + let imgUrl: String +} + +extension ImageUrlInfo { + + static var defaultValue: ImageUrlInfo = ImageUrlInfo(imgName: "", imgUrl: "") +} + +extension ImageUrlInfo: Decodable { } diff --git a/SOOUM/SOOUM/Domain/Models/SignUpRequestInfo.swift b/SOOUM/SOOUM/Domain/Models/SignUpRequestInfo.swift new file mode 100644 index 00000000..d2272f9d --- /dev/null +++ b/SOOUM/SOOUM/Domain/Models/SignUpRequestInfo.swift @@ -0,0 +1,37 @@ +// +// SignUpRequestInfo.swift +// SOOUM +// +// Created by 오현식 on 9/16/25. +// + +import Foundation + +struct MemberInfo: Equatable { + + let encryptedDeviceId: String + let deviceType: String + let fcmToken: String + let isNotificationAgreed: Bool +} + +extension MemberInfo { + + init(encryptedDeviceId: String, fcmToken: String, isNotificationAgreed: Bool) { + self.encryptedDeviceId = encryptedDeviceId + self.deviceType = "IOS" + self.fcmToken = fcmToken + self.isNotificationAgreed = isNotificationAgreed + } +} + +extension MemberInfo: Encodable { } + +struct Policy: Equatable { + + let agreedToTermsOfService: Bool + let agreedToLocationTerms: Bool + let agreedToPrivacyPolicy: Bool +} + +extension Policy: Encodable { } diff --git a/SOOUM/SOOUM/Domain/Models/Token.swift b/SOOUM/SOOUM/Domain/Models/Token.swift new file mode 100644 index 00000000..3c7bd006 --- /dev/null +++ b/SOOUM/SOOUM/Domain/Models/Token.swift @@ -0,0 +1,19 @@ +// +// Token.swift +// SOOUM +// +// Created by 오현식 on 9/17/25. +// + +import Foundation + +struct Token: Decodable { + + var accessToken: String + var refreshToken: String +} + +extension Token { + + static var defaultValue: Token = Token(accessToken: "", refreshToken: "") +} diff --git a/SOOUM/SOOUM/Managers/NetworkManager/Models/Version.swift b/SOOUM/SOOUM/Domain/Models/Version.swift similarity index 75% rename from SOOUM/SOOUM/Managers/NetworkManager/Models/Version.swift rename to SOOUM/SOOUM/Domain/Models/Version.swift index 909df9b8..2114a221 100644 --- a/SOOUM/SOOUM/Managers/NetworkManager/Models/Version.swift +++ b/SOOUM/SOOUM/Domain/Models/Version.swift @@ -7,13 +7,18 @@ import Foundation - struct Version { + let currentVersionStatus: Status +} + +extension Version { init(status: String) { self.currentVersionStatus = Status(rawValue: status) ?? .NONE } + + static var defaultValue: Version = Version(status: "NONE") } extension Version { @@ -43,3 +48,11 @@ extension Version { self.currentVersionStatus == .PENDING } } + +extension Version.Status: Decodable { } +extension Version: Decodable { + + enum CodingKeys: String, CodingKey { + case currentVersionStatus = "status" + } +} diff --git a/SOOUM/SOOUM/Domain/Repositories/AppVersionRepository.swift b/SOOUM/SOOUM/Domain/Repositories/AppVersionRepository.swift new file mode 100644 index 00000000..98648ecd --- /dev/null +++ b/SOOUM/SOOUM/Domain/Repositories/AppVersionRepository.swift @@ -0,0 +1,16 @@ +// +// AppVersionRepository.swift +// SOOUM +// +// Created by 오현식 on 9/16/25. +// + +import Foundation + +import RxSwift + +protocol AppVersionRepository { + + func version() -> Observable + func oldVersion() -> Observable +} diff --git a/SOOUM/SOOUM/Domain/Repositories/AuthRepository.swift b/SOOUM/SOOUM/Domain/Repositories/AuthRepository.swift new file mode 100644 index 00000000..5b74c4f6 --- /dev/null +++ b/SOOUM/SOOUM/Domain/Repositories/AuthRepository.swift @@ -0,0 +1,20 @@ +// +// AuthRepository.swift +// SOOUM +// +// Created by 오현식 on 9/17/25. +// + +import Foundation + +import RxSwift + +protocol AuthRepository { + + func signUp(nickname: String, profileImageName: String?) -> Observable + func login() -> Observable + + func initializeAuthInfo() + func hasToken() -> Bool + func tokens() -> Token +} diff --git a/SOOUM/SOOUM/Domain/Repositories/NotificationRepository.swift b/SOOUM/SOOUM/Domain/Repositories/NotificationRepository.swift new file mode 100644 index 00000000..ef9ba71a --- /dev/null +++ b/SOOUM/SOOUM/Domain/Repositories/NotificationRepository.swift @@ -0,0 +1,23 @@ +// +// NotificationRepository.swift +// SOOUM +// +// Created by 오현식 on 9/17/25. +// + +import Foundation + +import RxSwift + +protocol NotificationRepository { + + func unreadNotifications(lastId: String?) -> Observable<[NotificationInfoResponse]> + func unreadCardNotifications(lastId: String?) -> Observable<[NotificationInfoResponse]> + func unreadFollowNotifications(lastId: String?) -> Observable<[NotificationInfoResponse]> + func unreadNoticeNoticeNotifications(lastId: String?) -> Observable<[NotificationInfoResponse]> + func readNotifications(lastId: String?) -> Observable<[NotificationInfoResponse]> + func readCardNotifications(lastId: String?) -> Observable<[NotificationInfoResponse]> + func readFollowNotifications(lastId: String?) -> Observable<[NotificationInfoResponse]> + func readNoticeNoticeNotifications(lastId: String?) -> Observable<[NotificationInfoResponse]> + func requestRead(notificationId: String) -> Observable +} diff --git a/SOOUM/SOOUM/Domain/Repositories/UserRepository.swift b/SOOUM/SOOUM/Domain/Repositories/UserRepository.swift new file mode 100644 index 00000000..515f49ce --- /dev/null +++ b/SOOUM/SOOUM/Domain/Repositories/UserRepository.swift @@ -0,0 +1,22 @@ +// +// UserRepository.swift +// SOOUM +// +// Created by 오현식 on 9/17/25. +// + +import Foundation + +import RxSwift + +protocol UserRepository { + + func checkAvailable() -> Observable + func nickname() -> Observable + func validateNickname(nickname: String) -> Observable + func updateNickname(nickname: String) -> Observable + func presignedURL() -> Observable + func uploadImage(_ data: Data, with url: URL) -> Observable> + func updateImage(imageName: String) -> Observable + func updateFCMToken(fcmToken: String) -> Observable +} diff --git a/SOOUM/SOOUM/Domain/UseCases/AppVersionUseCaseImpl.swift b/SOOUM/SOOUM/Domain/UseCases/AppVersionUseCaseImpl.swift new file mode 100644 index 00000000..249d4257 --- /dev/null +++ b/SOOUM/SOOUM/Domain/UseCases/AppVersionUseCaseImpl.swift @@ -0,0 +1,29 @@ +// +// AppVersionUseCaseImpl.swift +// SOOUM +// +// Created by 오현식 on 9/16/25. +// + +import Foundation + +import RxSwift + +class AppVersionUseCaseImpl: AppVersionUseCase { + + private let repository: AppVersionRepository + + init(repository: AppVersionRepository) { + self.repository = repository + } + + func version() -> Observable { + + return self.repository.version().map { $0.version } + } + + func oldVersion() -> Observable { + + return self.repository.oldVersion().map { Version(status: $0) } + } +} diff --git a/SOOUM/SOOUM/Domain/UseCases/AuthUseCaseImpl.swift b/SOOUM/SOOUM/Domain/UseCases/AuthUseCaseImpl.swift new file mode 100644 index 00000000..ab69f779 --- /dev/null +++ b/SOOUM/SOOUM/Domain/UseCases/AuthUseCaseImpl.swift @@ -0,0 +1,44 @@ +// +// AuthUseCaseImpl.swift +// SOOUM +// +// Created by 오현식 on 9/17/25. +// + +import Foundation + +import RxSwift + +class AuthUseCaseImpl: AuthUseCase { + + private let repository: AuthRepository + + init(repository: AuthRepository) { + self.repository = repository + } + + func signUp(nickname: String, profileImageName: String?) -> Observable { + + return self.repository.signUp(nickname: nickname, profileImageName: profileImageName) + } + + func login() -> Observable { + + return self.repository.login() + } + + func initializeAuthInfo() { + + self.repository.initializeAuthInfo() + } + + func hasToken() -> Bool { + + return self.repository.hasToken() + } + + func tokens() -> Token { + + return self.repository.tokens() + } +} diff --git a/SOOUM/SOOUM/Domain/UseCases/Interfaces/AppVersionUseCase.swift b/SOOUM/SOOUM/Domain/UseCases/Interfaces/AppVersionUseCase.swift new file mode 100644 index 00000000..f9b1c5e3 --- /dev/null +++ b/SOOUM/SOOUM/Domain/UseCases/Interfaces/AppVersionUseCase.swift @@ -0,0 +1,16 @@ +// +// AppVersionUseCase.swift +// SOOUM +// +// Created by 오현식 on 9/16/25. +// + +import Foundation + +import RxSwift + +protocol AppVersionUseCase { + + func version() -> Observable + func oldVersion() -> Observable +} diff --git a/SOOUM/SOOUM/Domain/UseCases/Interfaces/AuthUseCase.swift b/SOOUM/SOOUM/Domain/UseCases/Interfaces/AuthUseCase.swift new file mode 100644 index 00000000..9b4cb4e2 --- /dev/null +++ b/SOOUM/SOOUM/Domain/UseCases/Interfaces/AuthUseCase.swift @@ -0,0 +1,20 @@ +// +// AuthUseCase.swift +// SOOUM +// +// Created by 오현식 on 9/17/25. +// + +import Foundation + +import RxSwift + +protocol AuthUseCase { + + func signUp(nickname: String, profileImageName: String?) -> Observable + func login() -> Observable + + func initializeAuthInfo() + func hasToken() -> Bool + func tokens() -> Token +} diff --git a/SOOUM/SOOUM/Domain/UseCases/Interfaces/NotificationUseCase.swift b/SOOUM/SOOUM/Domain/UseCases/Interfaces/NotificationUseCase.swift new file mode 100644 index 00000000..25a030d6 --- /dev/null +++ b/SOOUM/SOOUM/Domain/UseCases/Interfaces/NotificationUseCase.swift @@ -0,0 +1,23 @@ +// +// NotificationUseCase.swift +// SOOUM +// +// Created by 오현식 on 9/17/25. +// + +import Foundation + +import RxSwift + +protocol NotificationUseCase { + + func unreadNotifications(lastId: String?) -> Observable<[NotificationInfoResponse]> + func unreadCardNotifications(lastId: String?) -> Observable<[NotificationInfoResponse]> + func unreadFollowNotifications(lastId: String?) -> Observable<[NotificationInfoResponse]> + func unreadNoticeNoticeNotifications(lastId: String?) -> Observable<[NotificationInfoResponse]> + func readNotifications(lastId: String?) -> Observable<[NotificationInfoResponse]> + func readCardNotifications(lastId: String?) -> Observable<[NotificationInfoResponse]> + func readFollowNotifications(lastId: String?) -> Observable<[NotificationInfoResponse]> + func readNoticeNoticeNotifications(lastId: String?) -> Observable<[NotificationInfoResponse]> + func requestRead(notificationId: String) -> Observable +} diff --git a/SOOUM/SOOUM/Domain/UseCases/Interfaces/UserUseCase.swift b/SOOUM/SOOUM/Domain/UseCases/Interfaces/UserUseCase.swift new file mode 100644 index 00000000..bc37df42 --- /dev/null +++ b/SOOUM/SOOUM/Domain/UseCases/Interfaces/UserUseCase.swift @@ -0,0 +1,22 @@ +// +// UserUseCase.swift +// SOOUM +// +// Created by 오현식 on 9/17/25. +// + +import Foundation + +import RxSwift + +protocol UserUseCase { + + func isAvailableCheck() -> Observable + func nickname() -> Observable + func isNicknameValid(nickname: String) -> Observable + func updateNickname(nickname: String) -> Observable + func presignedURL() -> Observable + func uploadImage(_ data: Data, with url: URL) -> Observable + func updateImage(imageName: String) -> Observable + func updateFCMToken(fcmToken: String) -> Observable +} diff --git a/SOOUM/SOOUM/Domain/UseCases/NotificationUseCaseImpl.swift b/SOOUM/SOOUM/Domain/UseCases/NotificationUseCaseImpl.swift new file mode 100644 index 00000000..6d9f818f --- /dev/null +++ b/SOOUM/SOOUM/Domain/UseCases/NotificationUseCaseImpl.swift @@ -0,0 +1,64 @@ +// +// NotificationUseCaseImpl.swift +// SOOUM +// +// Created by 오현식 on 9/17/25. +// + +import Foundation + +import RxSwift + +class NotificationUseCaseImpl: NotificationUseCase { + + private let repository: NotificationRepository + + init(repository: NotificationRepository) { + self.repository = repository + } + + func unreadNotifications(lastId: String?) -> Observable<[NotificationInfoResponse]> { + + return self.repository.unreadNotifications(lastId: lastId) + } + + func unreadCardNotifications(lastId: String?) -> Observable<[NotificationInfoResponse]> { + + return self.repository.unreadCardNotifications(lastId: lastId) + } + + func unreadFollowNotifications(lastId: String?) -> Observable<[NotificationInfoResponse]> { + + return self.repository.unreadFollowNotifications(lastId: lastId) + } + + func unreadNoticeNoticeNotifications(lastId: String?) -> Observable<[NotificationInfoResponse]> { + + return self.repository.unreadNoticeNoticeNotifications(lastId: lastId) + } + + func readNotifications(lastId: String?) -> Observable<[NotificationInfoResponse]> { + + return self.repository.readNotifications(lastId: lastId) + } + + func readCardNotifications(lastId: String?) -> Observable<[NotificationInfoResponse]> { + + return self.repository.readCardNotifications(lastId: lastId) + } + + func readFollowNotifications(lastId: String?) -> Observable<[NotificationInfoResponse]> { + + return self.repository.readFollowNotifications(lastId: lastId) + } + + func readNoticeNoticeNotifications(lastId: String?) -> Observable<[NotificationInfoResponse]> { + + return self.repository.readNoticeNoticeNotifications(lastId: lastId) + } + + func requestRead(notificationId: String) -> Observable { + + return self.repository.requestRead(notificationId: notificationId).map { $0 == 200 } + } +} diff --git a/SOOUM/SOOUM/Domain/UseCases/UserUseCaseImpl.swift b/SOOUM/SOOUM/Domain/UseCases/UserUseCaseImpl.swift new file mode 100644 index 00000000..bdfefb67 --- /dev/null +++ b/SOOUM/SOOUM/Domain/UseCases/UserUseCaseImpl.swift @@ -0,0 +1,61 @@ +// +// UserUseCaseImpl.swift +// SOOUM +// +// Created by 오현식 on 9/17/25. +// + +import Foundation + +import RxSwift + +class UserUseCaseImpl: UserUseCase { + + private let repository: UserRepository + + init(repository: UserRepository) { + self.repository = repository + } + + func isAvailableCheck() -> Observable { + + return self.repository.checkAvailable().map { $0.checkAvailable } + } + + func nickname() -> Observable { + + return self.repository.nickname().map { $0.nickname } + } + + func isNicknameValid(nickname: String) -> Observable { + + return self.repository.validateNickname(nickname: nickname).map { $0.isAvailable } + } + + func updateNickname(nickname: String) -> Observable { + + return self.repository.updateNickname(nickname: nickname).map { $0 == 200 } + } + + func presignedURL() -> Observable { + + return self.repository.presignedURL().map { $0.imageUrlInfo } + } + + func uploadImage(_ data: Data, with url: URL) -> Observable { + + return self.repository.uploadImage(data, with: url) + .map { _ in true } + .catchAndReturn(false) + } + + func updateImage(imageName: String) -> Observable { + + return self.repository.updateImage(imageName: imageName).map { $0 == 200 } + } + + func updateFCMToken(fcmToken: String) -> Observable { + + return self.repository.updateFCMToken(fcmToken: fcmToken).map { $0 == 200 } + } +} diff --git a/SOOUM/SOOUM/Models/Auth/BaseAuthResponse.swift b/SOOUM/SOOUM/Models/Auth/BaseAuthResponse.swift index 46471413..a8e77254 100644 --- a/SOOUM/SOOUM/Models/Auth/BaseAuthResponse.swift +++ b/SOOUM/SOOUM/Models/Auth/BaseAuthResponse.swift @@ -13,9 +13,3 @@ struct Links: Codable { let login: URLString? let home: URLString? } - -// MARK: - Token -struct Token: Codable { - var accessToken: String - var refreshToken: String -} diff --git a/SOOUM/SOOUM/Models/Auth/SignInResponse.swift b/SOOUM/SOOUM/Models/Auth/SignInResponse.swift index f7216b61..eec4b895 100644 --- a/SOOUM/SOOUM/Models/Auth/SignInResponse.swift +++ b/SOOUM/SOOUM/Models/Auth/SignInResponse.swift @@ -10,7 +10,7 @@ import Foundation import Alamofire -struct SignInResponse: Codable { +struct SignInResponse: Decodable { let status: Status let isRegistered: Bool let token: Token? diff --git a/SOOUM/SOOUM/Models/Auth/SignUpResponse.swift b/SOOUM/SOOUM/Models/Auth/SignUpResponse.swift deleted file mode 100644 index 526e6ba8..00000000 --- a/SOOUM/SOOUM/Models/Auth/SignUpResponse.swift +++ /dev/null @@ -1,38 +0,0 @@ -// -// SignUpResponse.swift -// SOOUM -// -// Created by JDeoks on 10/1/24. -// - -import Foundation - -import Alamofire - - -struct SignUpResponse: Codable { - let status: Status - let token: Token - let links: Links - - enum CodingKeys: String, CodingKey { - case status - case token - case links = "_links" - } -} - -extension SignUpResponse { - - init() { - self.status = .init() - self.token = .init(accessToken: "", refreshToken: "") - self.links = .init(login: nil, home: nil) - } -} - -extension SignUpResponse: EmptyResponse { - static func emptyValue() -> SignUpResponse { - SignUpResponse.init() - } -} diff --git a/SOOUM/SOOUM/Presentations/Intro/Launch/LaunchScreenViewController.swift b/SOOUM/SOOUM/Presentations/Intro/Launch/LaunchScreenViewController.swift index 13239661..ae7c7120 100644 --- a/SOOUM/SOOUM/Presentations/Intro/Launch/LaunchScreenViewController.swift +++ b/SOOUM/SOOUM/Presentations/Intro/Launch/LaunchScreenViewController.swift @@ -15,26 +15,20 @@ import SnapKit import Then -class LaunchScreenViewController: BaseViewController, View { +class LaunchScreenViewController: BaseNavigationViewController, View { enum Text { static let updateVerionTitle: String = "업데이트 안내" - static let updateVersionMessage: String = "안정적인 서비스 사용을 위해\n최신버전으로 업데이트해주세요" + static let updateVersionMessage: String = "새로운 버전이 출시되었습니다. 더 나은 사용을 위해, 서비스 업데이트 후 이용 바랍니다." static let testFlightStrUrl: String = "itms-beta://testflight.apple.com/v1/app" static let appStoreStrUrl: String = "itms-apps://itunes.apple.com/app/id" - static let exitActionTitle: String = "종료하기" - static let updateActionTitle: String = "업데이트" + static let updateActionTitle: String = "새로워진 숨 사용하기" } - let viewForAnimation = UIView().then { - $0.backgroundColor = UIColor(hex: "#A2E3FF") - } - - let imageView = UIImageView(image: .init(.logo)).then { + let imageView = UIImageView(image: .init(.logo(.v2(.logo_white)))).then { $0.contentMode = .scaleAspectFit - $0.tintColor = .som.white } override var preferredStatusBarStyle: UIStatusBarStyle { @@ -42,51 +36,34 @@ class LaunchScreenViewController: BaseViewController, View { } override func setupConstraints() { - self.view.backgroundColor = UIColor(hex: "#A2E3FF") + super.setupConstraints() + + self.isNavigationBarHidden = true + + self.view.backgroundColor = .som.v2.pMain self.view.addSubview(self.imageView) self.imageView.snp.makeConstraints { $0.centerX.equalToSuperview() $0.centerY.equalTo(self.view.safeAreaLayoutGuide.snp.centerY) - $0.width.equalTo(235) - $0.height.equalTo(45) - } - - self.view.addSubview(self.viewForAnimation) - self.viewForAnimation.snp.makeConstraints { - $0.edges.equalTo(self.imageView) + $0.width.equalTo(200) + $0.height.equalTo(33) } } func bind(reactor: LaunchScreenViewReactor) { - // 애니메이션이 끝나면 launch action - self.rx.viewDidLayoutSubviews - .subscribe(with: self) { object, _ in - object.animate(to: 45) { _ in - reactor.action.onNext(.launch) - } - } + self.rx.viewDidLoad + .map { _ in Reactor.Action.launch } + .bind(to: reactor.action) .disposed(by: self.disposeBag) // 앱 버전 검사 reactor.state.map(\.mustUpdate) .distinctUntilChanged() .filter { $0 } - .subscribe(with: self) { object, _ in + .subscribe(onNext: { _ in - let exitAction = SOMDialogAction( - title: Text.exitActionTitle, - style: .gray, - action: { - // 앱 종료 - // 자연스럽게 종료하기 위해 종료전, suspend 상태로 변경 후 종료 - UIApplication.shared.perform(#selector(NSXPCConnection.suspend)) - DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { - exit(0) - } - } - ) let updateAction = SOMDialogAction( title: Text.updateActionTitle, style: .primary, @@ -112,9 +89,10 @@ class LaunchScreenViewController: BaseViewController, View { SOMDialogViewController.show( title: Text.updateVerionTitle, message: Text.updateVersionMessage, - actions: [exitAction, updateAction] + textAlignment: .left, + actions: [updateAction] ) - } + }) .disposed(by: self.disposeBag) // 로그인 성공 시 홈 화면으로 전환 @@ -122,12 +100,12 @@ class LaunchScreenViewController: BaseViewController, View { isRegistered .filter { $0 == true } .subscribe(with: self) { object, _ in - let viewController = MainTabBarController() - viewController.reactor = reactor.reactorForMainTabBar() - let navigationController = UINavigationController( - rootViewController: viewController - ) - object.view.window?.rootViewController = navigationController + // let viewController = MainTabBarController() + // viewController.reactor = reactor.reactorForMainTabBar() + // let navigationController = UINavigationController( + // rootViewController: viewController + // ) + // object.view.window?.rootViewController = navigationController } .disposed(by: self.disposeBag) // 로그인 실패 시 온보딩 화면으로 전환 @@ -144,19 +122,3 @@ class LaunchScreenViewController: BaseViewController, View { .disposed(by: self.disposeBag) } } - -extension LaunchScreenViewController { - - private func animate(to height: CGFloat, completion: @escaping ((Bool) -> Void)) { - - UIView.animate( - withDuration: 0.5, - delay: 0.2, - options: [.beginFromCurrentState, .curveEaseOut], - animations: { - self.viewForAnimation.transform = .init(translationX: 0, y: height) - }, - completion: completion - ) - } -} diff --git a/SOOUM/SOOUM/Presentations/Intro/Launch/LaunchScreenViewReactor.swift b/SOOUM/SOOUM/Presentations/Intro/Launch/LaunchScreenViewReactor.swift index d1679f27..c0cd8e4e 100644 --- a/SOOUM/SOOUM/Presentations/Intro/Launch/LaunchScreenViewReactor.swift +++ b/SOOUM/SOOUM/Presentations/Intro/Launch/LaunchScreenViewReactor.swift @@ -38,10 +38,10 @@ class LaunchScreenViewReactor: Reactor { } struct State { - var mustUpdate: Bool + fileprivate(set) var mustUpdate: Bool /// deviceId 서버 등록 여부, 로그인 성공 여부 - var isRegistered: Bool? - var appFlag: Bool? + fileprivate(set) var isRegistered: Bool? + fileprivate(set) var appFlag: Bool? } var initialState: State = .init( @@ -49,11 +49,18 @@ class LaunchScreenViewReactor: Reactor { isRegistered: nil ) - let provider: ManagerProviderType - let pushInfo: NotificationInfo? + // TODO: 임시, 추후 Coordinator 패턴 적용 후 필요한 UseCase만 사용 + private let dependencies: AppDIContainerable + private let authUseCase: AuthUseCase + private let versionUseCase: AppVersionUseCase - init(provider: ManagerProviderType, pushInfo: NotificationInfo? = nil) { - self.provider = provider + private let pushInfo: NotificationInfo? + + init(dependencies: AppDIContainerable, pushInfo: NotificationInfo? = nil) { + self.dependencies = dependencies + self.authUseCase = dependencies.rootContainer.resolve(AuthUseCase.self) + self.versionUseCase = dependencies.rootContainer.resolve(AppVersionUseCase.self) + self.pushInfo = pushInfo } @@ -63,10 +70,12 @@ class LaunchScreenViewReactor: Reactor { // 계정 이관에 성공했을 때, 온보딩 화면으로 전환 let isTransfered = self.pushInfo?.isTransfered ?? false if isTransfered { - self.provider.authManager.initializeAuthInfo() + self.authUseCase.initializeAuthInfo() return .just(.updateIsRegistered(false)) + .delay(.milliseconds(500), scheduler: MainScheduler.instance) } else { return self.check() + .delay(.milliseconds(500), scheduler: MainScheduler.instance) } } } @@ -93,35 +102,61 @@ class LaunchScreenViewReactor: Reactor { extension LaunchScreenViewReactor { private func login() -> Observable { - return self.provider.authManager.certification() + return self.authUseCase.login() .map { .updateIsRegistered($0) } + .catch(self.catchClosure) } private func check() -> Observable { - return self.provider.networkManager.request(String.self, request: AuthRequest.updateCheck) + #if PRODUCTION + return self.versionUseCase.oldVersion() + .withUnretained(self) + .flatMapLatest { object, version -> Observable in + + UserDefaults.standard.set(version.shouldHideTransfer, forKey: "AppFlag") + + if version.mustUpdate { + return .just(.check(true)) + } else { + return object.authUseCase.hasToken() ? .just(.updateIsRegistered(true)) : object.login() + } + } + #elseif DEVELOP + return self.versionUseCase.version() .withUnretained(self) - .flatMapLatest { object, currentVersionStatus -> Observable in - let version = Version(status: currentVersionStatus) + .flatMapLatest { object, version -> Observable in UserDefaults.standard.set(version.shouldHideTransfer, forKey: "AppFlag") if version.mustUpdate { return .just(.check(true)) } else { - return self.provider.authManager.hasToken ? .just(.updateIsRegistered(true)) : object.login() + return object.authUseCase.hasToken() ? .just(.updateIsRegistered(true)) : object.login() } } + #endif + } +} + +private extension LaunchScreenViewReactor { + + private var catchClosure: ((Error) throws -> Observable ) { + return { error in + + let nsError = error as NSError + return nsError.code == 404 ? .just(.updateIsRegistered(false)) : .empty() + } } } extension LaunchScreenViewReactor { func reactorForOnboarding() -> OnboardingViewReactor { - OnboardingViewReactor(provider: self.provider) + OnboardingViewReactor(dependencies: self.dependencies) } - func reactorForMainTabBar() -> MainTabBarReactor { - MainTabBarReactor(provider: self.provider, pushInfo: self.pushInfo) - } + // func reactorForMainTabBar() -> MainTabBarReactor { + // MainTabBarReactor(provider: self.provider, pushInfo: self.pushInfo) + // } } diff --git a/SOOUM/SOOUM/Presentations/Intro/Onboarding/Completed/OnboardingCompletedViewController.swift b/SOOUM/SOOUM/Presentations/Intro/Onboarding/Completed/OnboardingCompletedViewController.swift new file mode 100644 index 00000000..a7b1e521 --- /dev/null +++ b/SOOUM/SOOUM/Presentations/Intro/Onboarding/Completed/OnboardingCompletedViewController.swift @@ -0,0 +1,130 @@ +// +// OnboardingCompletedViewController.swift +// SOOUM +// +// Created by 오현식 on 9/12/25. +// + +import UIKit + +import ReactorKit + +import SnapKit +import Then + +class OnboardingCompletedViewController: BaseNavigationViewController, View { + + enum Text { + static let title: String = "가입 완료" + static let message: String = "숨에 오신 걸 환영해요" + + static let confirmButtonTitle: String = "확인" + } + + + // MARK: Views + + private let imageView = UIImageView().then { + $0.image = .init(.image(.v2(.onboarding_finish))) + $0.contentMode = .scaleAspectFit + } + + private let titleLabel = UILabel().then { + $0.text = Text.title + $0.textColor = .som.v2.pDark + $0.typography = .som.v2.title2 + } + + private let messageLabel = UILabel().then { + $0.text = Text.message + $0.textColor = .som.v2.black + $0.typography = .som.v2.head1 + } + + private let confirmButton = SOMButton().then { + $0.title = Text.confirmButtonTitle + $0.typography = .som.v2.title1 + $0.foregroundColor = .som.v2.white + $0.backgroundColor = .som.v2.black + } + + + // MARK: Override func + + override func setupConstraints() { + super.setupConstraints() + + self.isNavigationBarHidden = true + + let container = UIStackView(arrangedSubviews: [ + self.imageView, + self.titleLabel, + self.messageLabel + ]).then { + $0.axis = .vertical + $0.alignment = .center + $0.setCustomSpacing(32, after: self.imageView) + $0.setCustomSpacing(4, after: self.titleLabel) + } + self.view.addSubview(container) + container.snp.makeConstraints { + $0.centerY.equalTo(self.view.safeAreaLayoutGuide.snp.centerY).offset(-56) + $0.centerX.equalToSuperview() + } + + self.view.addSubview(self.confirmButton) + self.confirmButton.snp.makeConstraints { + $0.bottom.equalTo(self.view.safeAreaLayoutGuide.snp.bottom) + $0.leading.equalToSuperview().offset(16) + $0.trailing.equalToSuperview().offset(-16) + $0.height.equalTo(56) + } + } + + func bind(reactor: OnboardingCompletedViewReactor) { + + // Action + self.confirmButton.rx.throttleTap + .subscribe(with: self) { object, _ in + + // TODO: 임시, 토큰 정보 삭제 후 계정 삭제 + let withdrawAction = SOMDialogAction( + title: "계정 삭제하기", + style: .primary, + action: { + + UIApplication.topViewController?.dismiss(animated: true) { + reactor.action.onNext(.withdraw) + } + } + ) + + SOMDialogViewController.show( + title: "온보딩/회원가입 플로우 완료", + message: "생성된 계정 정보를 삭제하고 다시 스플레쉬 화면부터 시작합니다.", + textAlignment: .left, + actions: [withdrawAction] + ) + + // let viewController = MainTabBarController() + // viewController.reactor = reactor.reactorForMainTabBar() + // let navigationController = UINavigationController( + // rootViewController: viewController + // ) + // // 제스처 뒤로가기를 위한 델리게이트 설정 + // navigationController.interactivePopGestureRecognizer?.delegate = self + // object.view.window?.rootViewController = navigationController + } + .disposed(by: self.disposeBag) + + reactor.state.map(\.isSuccess) + .distinctUntilChanged() + .filter { $0 } + .subscribe(with: self) { object, _ in + let viewController = LaunchScreenViewController() + viewController.reactor = reactor.reaactorForLaunch() + object.view.window?.rootViewController = viewController + } + .disposed(by: self.disposeBag) + } +} diff --git a/SOOUM/SOOUM/Presentations/Intro/Onboarding/Completed/OnboardingCompletedViewReactor.swift b/SOOUM/SOOUM/Presentations/Intro/Onboarding/Completed/OnboardingCompletedViewReactor.swift new file mode 100644 index 00000000..e3700b7b --- /dev/null +++ b/SOOUM/SOOUM/Presentations/Intro/Onboarding/Completed/OnboardingCompletedViewReactor.swift @@ -0,0 +1,73 @@ +// +// OnboardingCompletedViewReactor.swift +// SOOUM +// +// Created by 오현식 on 9/12/25. +// + +import ReactorKit + +import Alamofire + +class OnboardingCompletedViewReactor: Reactor { + + // typealias Action = NoAction + // typealias Mutation = NoMutation + + // struct State { } + // var initialState: State { .init() } + + enum Action { + case withdraw + } + + enum Mutation { + case withdraw(Bool) + } + + struct State { + var isSuccess: Bool + } + + var initialState: State = State(isSuccess: false) + + private let dependencies: AppDIContainerable + + init(dependencies: AppDIContainerable) { + self.dependencies = dependencies + } + + func mutate(action: Action) -> Observable { + switch action { + case .withdraw: + + let managers = self.dependencies.rootContainer.resolve(ManagerProviderType.self) + return managers.networkManager.perform(Empty.self, request: AuthRequest.withdraw(token: managers.authManager.authInfo.token)) + .map { _ in true } + .map(Mutation.withdraw) + } + } + + + func reduce(state: State, mutation: Mutation) -> State { + var state = state + switch mutation { + case .withdraw(let isSuccess): + state.isSuccess = isSuccess + self.dependencies.rootContainer.resolve(ManagerProviderType.self).authManager.initializeAuthInfo() + } + return state + } +} + +extension OnboardingCompletedViewReactor { + + // TODO: 임시, 계정 삭제 후 런치 화면으로 이동 + func reaactorForLaunch() -> LaunchScreenViewReactor { + LaunchScreenViewReactor(dependencies: self.dependencies) + } + + func reactorForMainTabBar() -> MainTabBarReactor { + MainTabBarReactor(provider: self.dependencies.rootContainer.resolve(ManagerProviderType.self)) + } +} diff --git a/SOOUM/SOOUM/Presentations/Intro/Onboarding/NicknameSetting/OnboardingNicknameSettingViewController.swift b/SOOUM/SOOUM/Presentations/Intro/Onboarding/NicknameSetting/OnboardingNicknameSettingViewController.swift index e829bfa1..cb5997c2 100644 --- a/SOOUM/SOOUM/Presentations/Intro/Onboarding/NicknameSetting/OnboardingNicknameSettingViewController.swift +++ b/SOOUM/SOOUM/Presentations/Intro/Onboarding/NicknameSetting/OnboardingNicknameSettingViewController.swift @@ -18,92 +18,58 @@ import RxSwift class OnboardingNicknameSettingViewController: BaseNavigationViewController, View { enum Text { - static let adjectives = [ - "공부하는", "생각하는", "사랑하는", "노래하는", - "요리하는", "운동하는", "여행하는", "대화하는", - "청소하는", "정리하는", "그리는", "사진하는", - "연구하는", "설계하는", "개발하는", "관리하는", - "발표하는", "수업하는", "교육하는", "상담하는", - "치료하는", "분석하는", "조사하는", "기록하는", - "편집하는", "제작하는", "수리하는", "판매하는", - "구매하는", "투자하는", "기획하는", "운영하는", - "지원하는", "협력하는", "참여하는", "소통하는", - "개선하는", "실천하는", "실험하는", "탐구하는", - "수집하는", "배달하는", "전달하는", "연결하는", - "조정하는", "선택하는", "결정하는", "준비하는", - "확인하는", "수업하는", "연습하는", "발표하는", - "기록하는", "정리하는", "대처하는", "해결하는", - "조율하는", "탐색하는", "분석하는", "실천하는" - ] - static let nouns = [ - "강아지", "고양이", "기린", "토끼", - "사자", "호랑이", "악어", "코끼리", - "판다", "부엉이", "까치", "앵무새", - "여우", "오리", "수달", "다람쥐", - "펭귄", "참새", "갈매기", "도마뱀", - "우산", "책상", "가방", "의자", - "시계", "안경", "컵", "접시", - "전화기", "자전거", "냉장고", "라디오", - "바나나", "케이크", "모자", "열쇠", - "지도", "구두", "텀블러", "바구니", - "공책", "거울", "청소기", "햄스터", - "낙타", "두더지", "돌고래", "문어", - "미어캣", "오소리", "다슬기", "해파리", - "원숭이", "홍학", "물개", "바다표", - "코뿔소", "물소", "개구리", "거북이" - ] + static let navigationTitle: String = "회원가입" - static let title: String = "반가워요!\n당신을 어떻게 부르면 될까요?" - static let message: String = "닉네임은 추후 변경이 가능해요" + static let title: String = "숨에서 사용할 닉네임을\n입력해주세요" + static let guideMessage: String = "최대 8자까지 입력할 수 있어요" - static let placeholder: String = "닉네임을 입력해주세요" - - static let confirmButtonTitle: String = "확인" + static let nextButtonTitle: String = "다음" } // MARK: Views - private let guideMessageView = OnboardingGuideMessageView(title: Text.title, message: Text.message) + private let guideMessageView = OnboardingGuideMessageView(title: Text.title, currentNumber: 2) - private let nicknameTextField = OnboardingNicknameTextFieldView().then { - $0.placeholder = Text.placeholder - } + private let nicknameTextField = OnboardingNicknameTextFieldView() private let nextButton = SOMButton().then { - $0.title = Text.confirmButtonTitle - $0.typography = .som.body1WithBold - $0.foregroundColor = .som.gray600 - - $0.backgroundColor = .som.gray300 - $0.layer.cornerRadius = 12 - $0.clipsToBounds = true + $0.title = Text.nextButtonTitle + $0.typography = .som.v2.title1 + $0.foregroundColor = .som.v2.white + $0.backgroundColor = .som.v2.black } // MARK: Override func + override func setupNaviBar() { + super.setupNaviBar() + + self.navigationBar.title = Text.navigationTitle + } + override func setupConstraints() { self.view.addSubview(self.guideMessageView) self.guideMessageView.snp.makeConstraints { - $0.top.equalTo(view.safeAreaLayoutGuide.snp.top).offset(28) - $0.leading.equalToSuperview().offset(20) - $0.trailing.equalToSuperview().offset(-20) + $0.top.equalTo(view.safeAreaLayoutGuide.snp.top).offset(16) + $0.leading.equalToSuperview().offset(16) + $0.trailing.equalToSuperview().offset(-16) } - self.view.addSubview(nicknameTextField) - nicknameTextField.snp.makeConstraints { - $0.top.equalTo(self.guideMessageView.snp.bottom).offset(24) + self.view.addSubview(self.nicknameTextField) + self.nicknameTextField.snp.makeConstraints { + $0.top.equalTo(self.guideMessageView.snp.bottom).offset(32) $0.leading.trailing.equalToSuperview() } self.view.addSubview(self.nextButton) self.nextButton.snp.makeConstraints { - $0.leading.equalToSuperview().offset(20) - $0.trailing.equalToSuperview().offset(-20) - $0.bottom.equalTo(view.safeAreaLayoutGuide).offset(-12) - $0.height.equalTo(48) + $0.bottom.equalTo(view.safeAreaLayoutGuide) + $0.leading.equalToSuperview().offset(16) + $0.trailing.equalToSuperview().offset(-16) + $0.height.equalTo(56) } } @@ -125,41 +91,44 @@ class OnboardingNicknameSettingViewController: BaseNavigationViewController, Vie // Action let nickname = self.nicknameTextField.textField.rx.text.orEmpty.distinctUntilChanged().share() nickname - .debounce(.seconds(1), scheduler: MainScheduler.instance) + .debounce(.milliseconds(500), scheduler: MainScheduler.instance) .map(Reactor.Action.checkValidate) .bind(to: reactor.action) - .disposed(by: disposeBag) + .disposed(by: self.disposeBag) self.rx.viewDidLoad - .map { _ in Text.adjectives.randomElement()! + " " + Text.nouns.randomElement()! } - .subscribe(with: self) { object, randomText in - object.nicknameTextField.text = randomText - object.nicknameTextField.textField.sendActions(for: .editingChanged) - } + .map { _ in Reactor.Action.landing } + .bind(to: reactor.action) .disposed(by: self.disposeBag) self.nextButton.rx.tap - .withLatestFrom(nickname) - .subscribe(with: self) { object, nickname in - let profileImageVC = ProfileImageSettingViewController() - profileImageVC.reactor = reactor.reactorForProfileImage(nickname: nickname) - object.navigationPush(profileImageVC, animated: true) + .subscribe(with: self) { object, _ in + let profileImageSettingVC = OnboardingProfileImageSettingViewController() + profileImageSettingVC.reactor = reactor.reactorForProfileImage() + object.navigationPush(profileImageSettingVC, animated: true) } - .disposed(by: disposeBag) + .disposed(by: self.disposeBag) // State + reactor.state.map(\.nickname) + .distinctUntilChanged() + .subscribe(with: self) { object, randomText in + object.nicknameTextField.text = randomText + object.nicknameTextField.textField.sendActions(for: .editingChanged) + } + .disposed(by: self.disposeBag) + reactor.state.map(\.isValid) .distinctUntilChanged() - .subscribe(with: self, onNext: { object, isValid in - object.nextButton.foregroundColor = isValid ? .som.white : .som.gray600 - object.nextButton.backgroundColor = isValid ? .som.p300 : .som.gray300 - object.nextButton.isEnabled = isValid - }) - .disposed(by: disposeBag) + .bind(to: self.nextButton.rx.isEnabled) + .disposed(by: self.disposeBag) reactor.state.map(\.errorMessage) .distinctUntilChanged() - .bind(to: self.nicknameTextField.rx.errorMessage) + .subscribe(with: self) { object, errorMessage in + object.nicknameTextField.guideMessage = errorMessage == nil ? Text.guideMessage : errorMessage + object.nicknameTextField.hasError = errorMessage != nil + } .disposed(by: self.disposeBag) } } diff --git a/SOOUM/SOOUM/Presentations/Intro/Onboarding/NicknameSetting/OnboardingNicknameSettingViewReactor.swift b/SOOUM/SOOUM/Presentations/Intro/Onboarding/NicknameSetting/OnboardingNicknameSettingViewReactor.swift index 7fd2922a..afdb6282 100644 --- a/SOOUM/SOOUM/Presentations/Intro/Onboarding/NicknameSetting/OnboardingNicknameSettingViewReactor.swift +++ b/SOOUM/SOOUM/Presentations/Intro/Onboarding/NicknameSetting/OnboardingNicknameSettingViewReactor.swift @@ -11,55 +11,105 @@ import RxSwift class OnboardingNicknameSettingViewReactor: Reactor { + enum Text { + static let adjectives = [ + "공부하는", "생각하는", "사랑하는", "노래하는", + "요리하는", "운동하는", "여행하는", "대화하는", + "청소하는", "정리하는", "그리는", "사진하는", + "연구하는", "설계하는", "개발하는", "관리하는", + "발표하는", "수업하는", "교육하는", "상담하는", + "치료하는", "분석하는", "조사하는", "기록하는", + "편집하는", "제작하는", "수리하는", "판매하는", + "구매하는", "투자하는", "기획하는", "운영하는", + "지원하는", "협력하는", "참여하는", "소통하는", + "개선하는", "실천하는", "실험하는", "탐구하는", + "수집하는", "배달하는", "전달하는", "연결하는", + "조정하는", "선택하는", "결정하는", "준비하는", + "확인하는", "수업하는", "연습하는", "발표하는", + "기록하는", "정리하는", "대처하는", "해결하는", + "조율하는", "탐색하는", "분석하는", "실천하는" + ] + static let nouns = [ + "강아지", "고양이", "기린", "토끼", + "사자", "호랑이", "악어", "코끼리", + "판다", "부엉이", "까치", "앵무새", + "여우", "오리", "수달", "다람쥐", + "펭귄", "참새", "갈매기", "도마뱀", + "우산", "책상", "가방", "의자", + "시계", "안경", "컵", "접시", + "전화기", "자전거", "냉장고", "라디오", + "바나나", "케이크", "모자", "열쇠", + "지도", "구두", "텀블러", "바구니", + "공책", "거울", "청소기", "햄스터", + "낙타", "두더지", "돌고래", "문어", + "미어캣", "오소리", "다슬기", "해파리", + "원숭이", "홍학", "물개", "바다표", + "코뿔소", "물소", "개구리", "거북이" + ] + } + enum ErrorMessages: String { case isEmpty = "한글자 이상 입력해주세요" case inValid = "부적절한 닉네임입니다. 다시 입력해주세요" } enum Action { + case landing case checkValidate(String) } enum Mutation { + case updateNickname(String) case updateIsValid(Bool) case updateIsErrorMessage(String?) } struct State { - var isValid: Bool - var errorMessage: String? + fileprivate(set) var nickname: String + fileprivate(set) var isValid: Bool + fileprivate(set) var errorMessage: String? } var initialState: State = .init( + nickname: "\(Text.adjectives.randomElement()!) \(Text.nouns.randomElement()!)", isValid: false, errorMessage: nil ) - let provider: ManagerProviderType + private let dependencies: AppDIContainerable + private let userUseCase: UserUseCase - init(provider: ManagerProviderType) { - self.provider = provider + init(dependencies: AppDIContainerable) { + self.dependencies = dependencies + self.userUseCase = dependencies.rootContainer.resolve(UserUseCase.self) } func mutate(action: Action) -> Observable { switch action { + case .landing: + + return self.userUseCase.nickname() + .map(Mutation.updateNickname) case let .checkValidate(nickname): + if nickname.isEmpty { return .concat([ .just(.updateIsValid(false)), .just(.updateIsErrorMessage(ErrorMessages.isEmpty.rawValue)) ]) } - let request: JoinRequest = .validateNickname(nickname: nickname) return .concat([ .just(.updateIsErrorMessage(nil)), - self.provider.networkManager.request(NicknameValidationResponse.self, request: request) - .flatMapLatest { response -> Observable in - let isAvailable = response.isAvailable - let errorMessage = isAvailable ? nil : ErrorMessages.inValid.rawValue + self.userUseCase.isNicknameValid(nickname: nickname) + .withUnretained(self) + .flatMapLatest { object, isValid -> Observable in + + let errorMessage = isValid ? nil : ErrorMessages.inValid.rawValue + let nickname = isValid ? nickname : object.currentState.nickname return .concat([ - .just(.updateIsValid(isAvailable)), + .just(.updateIsValid(isValid)), + .just(.updateNickname(nickname)), .just(.updateIsErrorMessage(errorMessage)) ]) } @@ -70,6 +120,8 @@ class OnboardingNicknameSettingViewReactor: Reactor { func reduce(state: State, mutation: Mutation) -> State { var newState = state switch mutation { + case let .updateNickname(nickname): + newState.nickname = nickname case let .updateIsValid(isValid): newState.isValid = isValid case let .updateIsErrorMessage(errorMessage): @@ -81,7 +133,10 @@ class OnboardingNicknameSettingViewReactor: Reactor { extension OnboardingNicknameSettingViewReactor { - func reactorForProfileImage(nickname: String) -> ProfileImageSettingViewReactor { - ProfileImageSettingViewReactor(provider: self.provider, nickname: nickname) + func reactorForProfileImage() -> OnboardingProfileImageSettingViewReactor { + OnboardingProfileImageSettingViewReactor( + dependencies: self.dependencies, + nickname: self.currentState.nickname + ) } } diff --git a/SOOUM/SOOUM/Presentations/Intro/Onboarding/NicknameSetting/Views/OnboardingNicknameTextFieldView.swift b/SOOUM/SOOUM/Presentations/Intro/Onboarding/NicknameSetting/Views/OnboardingNicknameTextFieldView.swift index 32641fbb..7a5fbd08 100644 --- a/SOOUM/SOOUM/Presentations/Intro/Onboarding/NicknameSetting/Views/OnboardingNicknameTextFieldView.swift +++ b/SOOUM/SOOUM/Presentations/Intro/Onboarding/NicknameSetting/Views/OnboardingNicknameTextFieldView.swift @@ -17,8 +17,8 @@ class OnboardingNicknameTextFieldView: UIView { // MARK: Views private lazy var textFieldBackgroundView = UIView().then { - $0.backgroundColor = .som.gray50 - $0.layer.cornerRadius = 12 + $0.backgroundColor = .som.v2.gray100 + $0.layer.cornerRadius = 10 let gestureRecognizer = UITapGestureRecognizer( target: self, @@ -30,9 +30,9 @@ class OnboardingNicknameTextFieldView: UIView { lazy var textField = UITextField().then { let paragraphStyle = NSMutableParagraphStyle() $0.defaultTextAttributes[.paragraphStyle] = paragraphStyle - $0.defaultTextAttributes[.foregroundColor] = UIColor.som.black - $0.defaultTextAttributes[.font] = Typography.som.body1WithRegular.font - $0.tintColor = .som.p300 + $0.defaultTextAttributes[.foregroundColor] = UIColor.som.v2.black + $0.defaultTextAttributes[.font] = Typography.som.v2.subtitle1.font + $0.tintColor = UIColor.som.v2.black $0.enablesReturnKeyAutomatically = true $0.returnKeyType = .go @@ -47,33 +47,26 @@ class OnboardingNicknameTextFieldView: UIView { $0.delegate = self } - private let errorMessageContainer = UIStackView().then { + private let guideMessageContainer = UIStackView().then { $0.axis = .horizontal $0.alignment = .center - $0.distribution = .equalSpacing - $0.spacing = 4 - - $0.isHidden = true + $0.spacing = 6 } private let errorImageView = UIImageView().then { - $0.image = .init(.image(.errorTriangle)) - $0.tintColor = .som.red - } - - private let errorMessageLabel = UILabel().then { - $0.textColor = .som.red - $0.typography = .som.body2WithBold + $0.image = .init(.icon(.v2(.outlined(.error)))) + $0.tintColor = .som.v2.rMain + $0.isHidden = true } - private let characterLabel = UILabel().then { - $0.textColor = .som.gray500 - $0.typography = .som.body1WithRegular + private let guideMessageLabel = UILabel().then { + $0.textColor = .som.v2.gray500 + $0.typography = .som.v2.caption2 } private lazy var clearButton = SOMButton().then { - $0.image = .init(.icon(.outlined(.cancel))) - $0.foregroundColor = .som.black + $0.image = .init(.icon(.v2(.outlined(.delete)))) + $0.foregroundColor = .som.v2.gray500 let gestureRecognizer = UITapGestureRecognizer( target: self, @@ -96,33 +89,22 @@ class OnboardingNicknameTextFieldView: UIView { } } - var placeholder: String? { + var guideMessage: String? { set { - if let string: String = newValue { - self.textField.attributedPlaceholder = NSAttributedString( - string: string, - attributes: [ - .foregroundColor: UIColor.som.gray500, - .font: Typography.som.body1WithRegular.font - ] - ) - } else { - self.textField.attributedPlaceholder = nil - } + self.guideMessageLabel.text = newValue } - get { - return self.textField.attributedPlaceholder?.string + return self.guideMessageLabel.text } } - var errorMessage: String? { + var hasError: Bool { set { - self.errorMessageContainer.isHidden = newValue == nil - self.errorMessageLabel.text = newValue + self.errorImageView.isHidden = newValue == false + self.guideMessageLabel.textColor = newValue == false ? .som.v2.gray500 : .som.v2.rMain } get { - return self.errorMessageLabel.text + self.errorImageView.isHidden == false } } @@ -185,43 +167,37 @@ class OnboardingNicknameTextFieldView: UIView { self.addSubview(self.textFieldBackgroundView) self.textFieldBackgroundView.snp.makeConstraints { $0.top.equalToSuperview() - $0.leading.equalToSuperview().offset(20) - $0.trailing.equalToSuperview().offset(-20) - $0.height.equalTo(52) + $0.leading.equalToSuperview().offset(16) + $0.trailing.equalToSuperview().offset(-16) + $0.height.equalTo(54) } self.textFieldBackgroundView.addSubview(self.textField) self.textField.snp.makeConstraints { $0.centerY.equalToSuperview() - $0.leading.equalToSuperview().offset(12) + $0.leading.equalToSuperview().offset(24) } self.textFieldBackgroundView.addSubview(self.clearButton) self.clearButton.snp.makeConstraints { $0.centerY.equalToSuperview() - $0.leading.equalTo(self.textField.snp.trailing).offset(9) - $0.trailing.equalToSuperview().offset(-8) - $0.size.equalTo(32) + $0.leading.equalTo(self.textField.snp.trailing).offset(14) + $0.trailing.equalToSuperview().offset(-24) + $0.size.equalTo(24) } - self.addSubview(self.errorMessageContainer) - self.errorMessageContainer.snp.makeConstraints { - $0.top.equalTo(self.textFieldBackgroundView.snp.bottom).offset(10) + self.addSubview(self.guideMessageContainer) + self.guideMessageContainer.snp.makeConstraints { + $0.top.equalTo(self.textFieldBackgroundView.snp.bottom).offset(8) $0.bottom.equalToSuperview() - $0.leading.equalToSuperview().offset(20) - $0.height.equalTo(24) + $0.leading.equalToSuperview().offset(16) + $0.trailing.lessThanOrEqualToSuperview().offset(-16) + $0.height.equalTo(18) } - self.errorMessageContainer.addArrangedSubview(self.errorImageView) + self.guideMessageContainer.addArrangedSubview(self.errorImageView) self.errorImageView.snp.makeConstraints { - $0.size.equalTo(24) - } - self.errorMessageContainer.addArrangedSubview(self.errorMessageLabel) - - self.addSubview(self.characterLabel) - self.characterLabel.snp.makeConstraints { - $0.top.equalTo(self.textFieldBackgroundView.snp.bottom).offset(12) - $0.leading.greaterThanOrEqualTo(self.errorMessageContainer.snp.trailing).offset(20) - $0.trailing.equalToSuperview().offset(-20) + $0.size.equalTo(16) } + self.guideMessageContainer.addArrangedSubview(self.guideMessageLabel) } } @@ -248,13 +224,6 @@ extension OnboardingNicknameTextFieldView: UITextFieldDelegate { return newString.count < self.maxCharacter + 1 } - func textFieldDidChangeSelection(_ textField: UITextField) { - - let text = textField.text ?? "" - self.clearButton.isHidden = text.isEmpty - self.characterLabel.text = text.count.description + "/" + self.maxCharacter.description - } - func textFieldShouldReturn(_ textField: UITextField) -> Bool { textField.resignFirstResponder() return true diff --git a/SOOUM/SOOUM/Presentations/Intro/Onboarding/Onboarding/OnboardingViewController.swift b/SOOUM/SOOUM/Presentations/Intro/Onboarding/Onboarding/OnboardingViewController.swift index 960cc018..905f1b49 100644 --- a/SOOUM/SOOUM/Presentations/Intro/Onboarding/Onboarding/OnboardingViewController.swift +++ b/SOOUM/SOOUM/Presentations/Intro/Onboarding/Onboarding/OnboardingViewController.swift @@ -18,12 +18,23 @@ import Then class OnboardingViewController: BaseNavigationViewController, View { enum Text { - static let guideText: String = "당신의 소중한 이야기를\n익명의 친구들에게 들려주세요" - static let startButtonText: String = "숨 시작하기" - static let oldUserButtonText: String = "기존 계정이 있으신가요?" + static let guideTitle: String = "숨겨진 진심이 모이는 공간" + static let guideSubTitle: String = "당신의 이야기를 편하게 남겨요" - static let banUserDialogTitle: String = "기존 정지된 계정으로\n가입이 불가능 합니다." - static let resignDialogTitle: String = "최근 탈퇴한 이력이 있습니다." + static let firstGuideMessage: String = "숨은 가입 시 어떤 개인정보도 요구하지 않아요" + static let secondGuideMessage: String = "자동으로 추천되는 닉네임으로 5초면 가입해요" + static let thirdGuideMessage: String = "익명으로 솔직한 이야기를 나눠요" + + static let startButtonTitle: String = "숨 시작하기" + static let oldUserButtontitle: String = "기존 계정이 있으신가요?" + + static let banUserDialogTitle: String = "가입할 수 없는 계정이에요" + static let banUserDialogLeadingMessage: String = "이 계정은 정지된 이력이 있습니다. 새 계정은 " + + static let resignDialogTitle: String = "최근 탈퇴한 계정이에요" + static let resignDialogLeadingMessage: String = "탈퇴일로부터 7일 후 새 계정을 만들 수 있습니다. 새 계정은 " + + static let dialogTrailingMessage: String = "부터 만들 수 있습니다." static let confirmActionTitle: String = "확인" } @@ -31,50 +42,61 @@ class OnboardingViewController: BaseNavigationViewController, View { // MARK: Views - private let backgroundImageView = UIImageView().then { - $0.image = .init(.image(.login)) - $0.contentMode = .scaleAspectFill + private let guideTitleLabel = UILabel().then { + $0.text = Text.guideTitle + $0.textColor = .som.v2.black + $0.typography = .som.v2.head1 } - private let guideLabel = UILabel().then { - $0.text = Text.guideText - $0.textColor = .som.p300 - $0.typography = .init( - fontContainer: BuiltInFont(size: 22, weight: .semibold), - lineHeight: 35, - letterSpacing: 0.05, - alignment: .left - ) - $0.numberOfLines = 0 + private let guideSubTitleLabel = UILabel().then { + $0.text = Text.guideSubTitle + $0.textColor = .som.v2.gray500 + $0.typography = .som.v2.title2 + } + + private let onboardingImageView = UIImageView().then { + $0.image = .init(.image(.v2(.onboarding))) + $0.contentMode = .scaleAspectFit + } + + private let guideMessageContainer = UIStackView().then { + $0.axis = .vertical + $0.alignment = .fill + $0.distribution = .equalSpacing + $0.spacing = 6 } private let startButton = SOMButton().then { - $0.title = Text.startButtonText - $0.typography = .init( - fontContainer: BuiltInFont(size: 16, weight: .heavy), - lineHeight: 20, - letterSpacing: 0.05 - ) - $0.foregroundColor = .som.white - - $0.backgroundColor = .som.p300 - $0.layer.cornerRadius = 12 - $0.clipsToBounds = true + $0.title = Text.startButtonTitle + $0.typography = .som.v2.title1 + $0.foregroundColor = .som.v2.white + $0.backgroundColor = .som.v2.black } private let oldUserButton = SOMButton().then { - $0.title = Text.oldUserButtonText - $0.typography = .init( - fontContainer: BuiltInFont(size: 14, weight: .bold), - lineHeight: 20, - letterSpacing: 0.05 - ) - $0.foregroundColor = UIColor(hex: "#B4B4B4") + $0.title = Text.oldUserButtontitle + $0.typography = .som.v2.body1 + $0.foregroundColor = .som.v2.gray500 $0.hasUnderlined = true + $0.inset = .init(top: 6, left: 16, bottom: 6, right: 16) } + // MARK: Override func + override func viewDidLoad() { + super.viewDidLoad() + + // 제스처 뒤로가기를 위한 델리게이트 설정 + self.navigationController?.interactivePopGestureRecognizer?.delegate = self + + self.setupGuideMessage([ + Text.firstGuideMessage, + Text.secondGuideMessage, + Text.thirdGuideMessage + ]) + } + override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) @@ -84,31 +106,46 @@ class OnboardingViewController: BaseNavigationViewController, View { override func setupConstraints() { super.setupConstraints() - self.view.addSubview(self.backgroundImageView) - self.backgroundImageView.snp.makeConstraints { - $0.edges.equalToSuperview() + self.view.addSubview(self.guideTitleLabel) + self.guideTitleLabel.snp.makeConstraints { + $0.top.equalTo(self.view.safeAreaLayoutGuide.snp.top).offset(60) + $0.leading.equalToSuperview().offset(16) + $0.trailing.lessThanOrEqualToSuperview().offset(-16) } - self.view.addSubview(self.guideLabel) - self.guideLabel.snp.makeConstraints { - /// 실 기기 높이 * 0.6 - $0.top.equalToSuperview().offset(UIScreen.main.bounds.height * 0.6) - $0.leading.equalToSuperview().offset(20) + self.view.addSubview(self.guideSubTitleLabel) + self.guideSubTitleLabel.snp.makeConstraints { + $0.top.equalTo(self.guideTitleLabel.snp.bottom).offset(4) + $0.leading.equalToSuperview().offset(16) + $0.trailing.lessThanOrEqualToSuperview().offset(-16) } - self.view.addSubview(self.startButton) - self.startButton.snp.makeConstraints { - $0.bottom.equalToSuperview().offset(-128) - $0.leading.equalToSuperview().offset(20) - $0.trailing.equalToSuperview().offset(-20) - $0.height.equalTo(56) + self.view.addSubview(self.onboardingImageView) + self.onboardingImageView.snp.makeConstraints { + $0.top.equalTo(self.guideSubTitleLabel.snp.bottom).offset(80) + $0.centerX.equalToSuperview() + } + + self.view.addSubview(self.guideMessageContainer) + self.guideMessageContainer.snp.makeConstraints { + $0.top.equalTo(self.onboardingImageView.snp.bottom).offset(60) + $0.leading.equalToSuperview().offset(16) + $0.trailing.equalToSuperview().offset(-16) } self.view.addSubview(self.oldUserButton) self.oldUserButton.snp.makeConstraints { - $0.top.equalTo(self.startButton.snp.bottom).offset(21) + $0.bottom.equalTo(self.view.safeAreaLayoutGuide.snp.bottom).offset(6) $0.centerX.equalToSuperview() } + + self.view.addSubview(self.startButton) + self.startButton.snp.makeConstraints { + $0.bottom.equalTo(self.oldUserButton.snp.top).offset(-14) + $0.leading.equalToSuperview().offset(16) + $0.trailing.equalToSuperview().offset(-16) + $0.height.equalTo(56) + } } @@ -122,17 +159,30 @@ class OnboardingViewController: BaseNavigationViewController, View { .bind(to: reactor.action) .disposed(by: self.disposeBag) - self.rx.viewWillAppear - .map { _ in Reactor.Action.reset } - .bind(to: reactor.action) - .disposed(by: self.disposeBag) + let startButtonTapped = self.startButton.rx.tap.share() + let checkAvailable = reactor.state.map(\.checkAvailable).share() - // Navigation - self.startButton.rx.tap - .map { _ in Reactor.Action.check } - .bind(to: reactor.action) + startButtonTapped + .withLatestFrom(checkAvailable) + .filter { $0 == nil } + .subscribe(with: self) { object, _ in + let termsOfServiceViewController = OnboardingTermsOfServiceViewController() + termsOfServiceViewController.reactor = reactor.reactorForTermsOfService() + object.navigationPush(termsOfServiceViewController, animated: true) + } .disposed(by: disposeBag) + startButtonTapped + .withLatestFrom(checkAvailable) + .filterNil() + .subscribe(with: self) { object, checkAvailable in + + if let rejoinAvailableAt = checkAvailable.rejoinAvailableAt { + object.showDialog(checkAvailable.banned, at: rejoinAvailableAt) + } + } + .disposed(by: self.disposeBag) + self.oldUserButton.rx.tap .subscribe(with: self) { object, _ in let enterMemberTransferViewController = EnterMemberTransferViewController() @@ -142,39 +192,18 @@ class OnboardingViewController: BaseNavigationViewController, View { .disposed(by: disposeBag) // State - reactor.state.map(\.suspension) + checkAvailable .filterNil() - .subscribe(with: self) { object, suspension in - let dialogMessageView = DialogMessageView( - isBanUser: suspension.isBanUser, - banDateString: suspension.untilBan.banEndFormatted - ) - - let confirmAction = SOMDialogAction( - title: Text.confirmActionTitle, - style: .primary, - action: { - UIApplication.topViewController?.dismiss(animated: true) - } - ) + .take(1) + .subscribe(with: self) { object, checkAvailable in - SOMDialogViewController.show( - title: suspension.isBanUser ? Text.banUserDialogTitle : Text.resignDialogTitle, - messageView: dialogMessageView, - actions: [confirmAction] - ) + if let rejoinAvailableAt = checkAvailable.rejoinAvailableAt { + object.showDialog(checkAvailable.banned, at: rejoinAvailableAt) + } } .disposed(by: self.disposeBag) - reactor.state.map(\.shouldNavigate) - .filter { $0 } - .subscribe(with: self) { object, _ in - let termsOfServiceViewController = OnboardingTermsOfServiceViewController() - termsOfServiceViewController.reactor = reactor.reactorForTermsOfService() - object.navigationPush(termsOfServiceViewController, animated: true) - } - .disposed(by: self.disposeBag) - + reactor.state.map(\.shouldHideTransfer) .subscribe(with: self) { object, shouldHide in object.oldUserButton.isHidden = shouldHide @@ -182,3 +211,54 @@ class OnboardingViewController: BaseNavigationViewController, View { .disposed(by: self.disposeBag) } } + +extension OnboardingViewController { + + func showDialog(_ isBanned: Bool, at rejoinAvailableAt: Date) { + let dialogLeadingMessage = isBanned ? Text.banUserDialogLeadingMessage : Text.resignDialogLeadingMessage + let dialogMessage = dialogLeadingMessage + rejoinAvailableAt.banEndFormatted + Text.dialogTrailingMessage + + let confirmAction = SOMDialogAction( + title: Text.confirmActionTitle, + style: .primary, + action: { + UIApplication.topViewController?.dismiss(animated: true) + } + ) + + SOMDialogViewController.show( + title: isBanned ? Text.banUserDialogTitle : Text.resignDialogTitle, + message: dialogMessage, + textAlignment: .left, + actions: [confirmAction] + ) + } + + func setupGuideMessage(_ messages: [String]) { + + messages.forEach { message in + + let imageView = UIImageView().then { + $0.image = .init(.image(.v2(.check_square_light))) + $0.setContentHuggingPriority(.defaultHigh, for: .horizontal) + } + + let label = UILabel().then { + $0.text = message + $0.textColor = .som.v2.gray400 + $0.typography = .som.v2.body1 + $0.textAlignment = .left + } + + let container = UIStackView(arrangedSubviews: [imageView, label]).then { + $0.axis = .horizontal + $0.spacing = 8 + } + container.snp.makeConstraints { + $0.height.equalTo(24) + } + + self.guideMessageContainer.addArrangedSubview(container) + } + } +} diff --git a/SOOUM/SOOUM/Presentations/Intro/Onboarding/Onboarding/OnboardingViewReactor.swift b/SOOUM/SOOUM/Presentations/Intro/Onboarding/Onboarding/OnboardingViewReactor.swift index 2b263d4d..67265c2e 100644 --- a/SOOUM/SOOUM/Presentations/Intro/Onboarding/Onboarding/OnboardingViewReactor.swift +++ b/SOOUM/SOOUM/Presentations/Intro/Onboarding/Onboarding/OnboardingViewReactor.swift @@ -12,31 +12,32 @@ class OnboardingViewReactor: Reactor { enum Action: Equatable { case landing - case reset - case check } enum Mutation { - case check(Suspension) - case shouldNavigate(Bool) + case check(CheckAvailable?) } struct State { - fileprivate(set) var suspension: Suspension? - fileprivate(set) var shouldNavigate: Bool = false + fileprivate(set) var checkAvailable: CheckAvailable? fileprivate(set) var shouldHideTransfer: Bool } var initialState: State = .init( - suspension: nil, + checkAvailable: nil, shouldHideTransfer: UserDefaults.standard.bool(forKey: "AppFlag") ) - let provider: ManagerProviderType + private let dependencies: AppDIContainerable + private let userUseCase: UserUseCase + private let pushManager: PushManagerDelegate - init(provider: ManagerProviderType) { - self.provider = provider + init(dependencies: AppDIContainerable) { + self.dependencies = dependencies + self.userUseCase = dependencies.rootContainer.resolve(UserUseCase.self) + let provider = dependencies.rootContainer.resolve(ManagerProviderType.self) + self.pushManager = provider.pushManager } func mutate(action: Action) -> Observable { @@ -45,29 +46,18 @@ class OnboardingViewReactor: Reactor { return .concat([ self.check() - .compactMap(\.value) - .map(Mutation.check), - self.provider.pushManager.switchNotification(on: true) + .compactMap(Mutation.check), + self.pushManager.switchNotification(on: true) .flatMapLatest { _ -> Observable in .empty() } ]) - case .reset: - - return .just(.shouldNavigate(false)) - case .check: - - return self.check() - .map { $0 == nil } - .map(Mutation.shouldNavigate) } } func reduce(state: State, mutation: Mutation) -> State { - var state = state + var newState = state switch mutation { - case let .check(suspension): - state.suspension = suspension - case let .shouldNavigate(shouldNavigate): - state.shouldNavigate = shouldNavigate + case let .check(checkAvailable): + newState.checkAvailable = checkAvailable } return state } @@ -75,36 +65,23 @@ class OnboardingViewReactor: Reactor { extension OnboardingViewReactor { - private func check() -> Observable { + private func check() -> Observable { - return self.provider.networkManager.request( - RSAKeyResponse.self, - request: AuthRequest.getPublicKey - ) - .map(\.publicKey) - .withUnretained(self) - .flatMapLatest { object, publicKey -> Observable in - - if let secKey = object.provider.authManager.convertPEMToSecKey(pemString: publicKey), - let encryptedDeviceId = object.provider.authManager.encryptUUIDWithPublicKey(publicKey: secKey) { + return self.userUseCase.isAvailableCheck() + .flatMapLatest { checkAvailable -> Observable in - let request: JoinRequest = .suspension(encryptedDeviceId: encryptedDeviceId) - return object.provider.networkManager.request(SuspensionResponse.self, request: request) - .map(\.suspension) - } else { - return .empty() + return checkAvailable == .defaultValue ? .just(nil) : .just(checkAvailable) } - } } } extension OnboardingViewReactor { func reactorForTermsOfService() -> OnboardingTermsOfServiceViewReactor { - OnboardingTermsOfServiceViewReactor(provider: self.provider) + OnboardingTermsOfServiceViewReactor(dependencies: self.dependencies) } func reactorForEnterTransfer() -> EnterMemberTransferViewReactor { - EnterMemberTransferViewReactor(provider: self.provider, entranceType: .onboarding) + EnterMemberTransferViewReactor(dependencies: self.dependencies, entranceType: .onboarding) } } diff --git a/SOOUM/SOOUM/Presentations/Intro/Onboarding/Onboarding/Views/DialogMessageView.swift b/SOOUM/SOOUM/Presentations/Intro/Onboarding/Onboarding/Views/DialogMessageView.swift deleted file mode 100644 index a11149c9..00000000 --- a/SOOUM/SOOUM/Presentations/Intro/Onboarding/Onboarding/Views/DialogMessageView.swift +++ /dev/null @@ -1,129 +0,0 @@ -// -// DialogMessageView.swift -// SOOUM -// -// Created by 오현식 on 1/18/25. -// - -import UIKit - -import SnapKit -import Then - - -class DialogMessageView: UIView { - - static var messageTypo: Typography = .som.body2WithBold - - enum Text { - static let dot: String = "•" - - static let banUserDialogFirstMessage: String = "해당 계정은 정지된 이력이 있는 탈퇴 계정 입니다." - static let resignDialogFirstMessage: String = "탈퇴 시점으로 부터 7일 경과 후 새로운 계정 생성이 가능합니다." - - static let dialogSecondLeftMessage: String = "새로운 계정 생성은 " - static let dialogSecondRightMessage: String = " 이후 가능합니다." - } - - - // MARK: Views - - private let firstDotLabel = UILabel().then { - $0.text = Text.dot - $0.textColor = .som.gray600 - $0.typography = .som.body2WithBold - - $0.setContentHuggingPriority(.defaultHigh, for: .horizontal) - } - private let firstMessageLabel = UILabel().then { - $0.textColor = .som.gray600 - $0.typography = .som.body2WithBold.withAlignment(.left) - $0.lineBreakMode = .byWordWrapping - $0.lineBreakStrategy = .hangulWordPriority - $0.numberOfLines = 0 - } - - private let secondDotLabel = UILabel().then { - $0.text = Text.dot - $0.textColor = .som.gray600 - $0.typography = .som.body2WithBold - - $0.setContentHuggingPriority(.defaultHigh, for: .horizontal) - } - private let secondMessageLabel = UILabel().then { - $0.textColor = .som.gray600 - $0.typography = .som.body2WithBold.withAlignment(.left) - $0.lineBreakMode = .byWordWrapping - $0.lineBreakStrategy = .hangulWordPriority - $0.numberOfLines = 0 - } - - - // MARK: Variables - - var firstMessage: String? { - set { - self.firstMessageLabel.text = newValue - self.firstMessageLabel.typography = Self.messageTypo.withAlignment(.left) - } - get { - return self.firstMessageLabel.text - } - } - - var secondMessage: String? { - set { - self.secondMessageLabel.text = newValue - self.secondMessageLabel.typography = Self.messageTypo.withAlignment(.left) - } - get { - return self.secondMessageLabel.text - } - } - - - // MARK: Initalization - - init(isBanUser: Bool, banDateString: String) { - super.init(frame: .zero) - - self.setupConstraints() - - self.firstMessage = isBanUser ? Text.banUserDialogFirstMessage : Text.resignDialogFirstMessage - self.secondMessage = Text.dialogSecondLeftMessage + banDateString + Text.dialogSecondRightMessage - } - - required init?(coder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - - - // MARK: Private Func - - private func setupConstraints() { - - self.addSubview(self.firstDotLabel) - self.firstDotLabel.snp.makeConstraints { - $0.top.leading.equalToSuperview() - } - - self.addSubview(self.firstMessageLabel) - self.firstMessageLabel.snp.makeConstraints { - $0.top.trailing.equalToSuperview() - $0.leading.equalTo(self.firstDotLabel.snp.trailing).offset(4) - } - - self.addSubview(self.secondDotLabel) - self.secondDotLabel.snp.makeConstraints { - $0.top.equalTo(self.firstMessageLabel.snp.bottom) - $0.leading.equalToSuperview() - } - - self.addSubview(self.secondMessageLabel) - self.secondMessageLabel.snp.makeConstraints { - $0.top.equalTo(self.firstMessageLabel.snp.bottom) - $0.bottom.trailing.equalToSuperview() - $0.leading.equalTo(self.secondDotLabel.snp.trailing).offset(4) - } - } -} diff --git a/SOOUM/SOOUM/Presentations/Intro/Onboarding/ProfileImageSetting/OnboardingProfileImageSettingViewController.swift b/SOOUM/SOOUM/Presentations/Intro/Onboarding/ProfileImageSetting/OnboardingProfileImageSettingViewController.swift new file mode 100644 index 00000000..e8f3e28e --- /dev/null +++ b/SOOUM/SOOUM/Presentations/Intro/Onboarding/ProfileImageSetting/OnboardingProfileImageSettingViewController.swift @@ -0,0 +1,287 @@ +// +// OnboardingProfileImageSettingViewController.swift +// SOOUM +// +// Created by JDeoks on 11/6/24. +// + +import UIKit + +import ReactorKit +import RxCocoa +import RxGesture +import RxSwift + +import SnapKit +import SwiftEntryKit +import Then +import YPImagePicker + + +class OnboardingProfileImageSettingViewController: BaseNavigationViewController, View { + + enum Text { + static let navigationTitle: String = "회원가입" + + static let title: String = "숨에서 사용할 프로필 사진을\n등록해주세요" + + static let completeButtonTitle: String = "완료" + static let passButtonTitle: String = "건너뛰기" + + static let inappositeDialogTitle: String = "부적절한 사진으로 보여져요" + static let inappositeDialogMessage: String = "다른 사진으로 변경하거나 기본 이미지를 사용해 주세요." + static let inappositeDialogConfirmButtonTitle: String = "확인" + + static let selectProfileEntryName: String = "selectProfile" + + static let selectProfileFirstButtonTitle: String = "앨범에서 사진 선택" + static let selectProfileSecondButtonTitle: String = "사진 찍기" + static let selectProfileThirdButtonTitle: String = "기본 이미지 적용" + + static let selectPhotoFullScreenNextTitle: String = "다음" + static let selectPhotoFullScreenCancelTitle: String = "취소" + static let selectPhotoFullScreenSaveTitle: String = "저장" + static let selectPhotoFullScreenAlbumsTitle: String = "앨범" + static let selectPhotoFullScreenCameraTitle: String = "카메라" + static let selectPhotoFullScreenLibraryTitle: String = "갤러리" + static let selectPhotoFullScreenCropTitle: String = "자르기" + } + + + // MARK: Views + + private let guideMessageView = OnboardingGuideMessageView(title: Text.title, currentNumber: 3) + + private let profileImageView = UIImageView().then { + $0.image = .init(.image(.v2(.profile))) + $0.layer.cornerRadius = 120 * 0.5 + $0.clipsToBounds = true + } + private let cameraButton = SOMButton().then { + $0.image = .init(.icon(.v2(.filled(.camera)))) + $0.foregroundColor = .som.v2.gray400 + + $0.backgroundColor = .som.v2.white + $0.layer.borderColor = UIColor.som.v2.gray200.cgColor + $0.layer.borderWidth = 1 + $0.layer.cornerRadius = 32 * 0.5 + } + + private let completeButton = SOMButton().then { + $0.title = Text.completeButtonTitle + $0.typography = .som.v2.title1 + $0.foregroundColor = .som.v2.white + $0.backgroundColor = .som.v2.black + } + + private let passButton = SOMButton().then { + $0.title = Text.passButtonTitle + $0.typography = .som.v2.title1 + $0.foregroundColor = .som.v2.gray600 + $0.backgroundColor = .som.v2.gray100 + } + + + // MARK: Variables + + private var actions: [SelectProfileBottomFloatView.FloatAction] = [] + + + // MARK: Override func + + override func setupConstraints() { + super.setupConstraints() + + self.view.addSubview(self.guideMessageView) + self.guideMessageView.snp.makeConstraints { + $0.top.equalTo(self.view.safeAreaLayoutGuide.snp.top).offset(16) + $0.leading.equalToSuperview().offset(16) + $0.trailing.equalToSuperview().offset(-16) + } + + self.view.addSubview(self.profileImageView) + self.profileImageView.snp.makeConstraints { + $0.top.equalTo(self.guideMessageView.snp.bottom).offset(32) + $0.centerX.equalToSuperview() + $0.size.equalTo(120) + } + self.view.addSubview(self.cameraButton) + self.cameraButton.snp.makeConstraints { + $0.bottom.equalTo(self.profileImageView.snp.bottom) + $0.trailing.equalTo(self.profileImageView.snp.trailing) + $0.size.equalTo(32) + } + + let container = UIStackView(arrangedSubviews: [self.passButton, self.completeButton]).then { + $0.axis = .horizontal + $0.spacing = 10 + } + self.view.addSubview(container) + container.snp.makeConstraints { + $0.bottom.equalTo(self.view.safeAreaLayoutGuide.snp.bottom).offset(-6) + $0.leading.equalToSuperview().offset(16) + $0.trailing.equalToSuperview().offset(-16) + $0.height.equalTo(56) + } + } + + + // MARK: ReactorKit - bind + + func bind(reactor: OnboardingProfileImageSettingViewReactor) { + + // Action + Observable.merge( + self.profileImageView.rx.tapGesture().when(.ended).map { _ in }, + self.cameraButton.rx.tap.asObservable() + ) + .subscribe(with: self) { object, _ in + let selectProfileBottomFloatView = SelectProfileBottomFloatView(actions: object.actions) + + var wrapper: SwiftEntryKitViewWrapper = selectProfileBottomFloatView.sek + wrapper.entryName = Text.selectProfileEntryName + wrapper.showBottomFloat(screenInteraction: .dismiss) + } + .disposed(by: self.disposeBag) + + self.completeButton.rx.tap + .map { _ in Reactor.Action.signUp } + .bind(to: reactor.action) + .disposed(by: self.disposeBag) + + self.passButton.rx.tap + .map { _ in Reactor.Action.signUp } + .bind(to: reactor.action) + .disposed(by: self.disposeBag) + + // State + reactor.state.map(\.isSignUp) + .distinctUntilChanged() + .filter { $0 } + .subscribe(with: self) { object, _ in + let viewController = OnboardingCompletedViewController() + viewController.reactor = reactor.reactorForCompleted() + object.navigationPush(viewController, animated: true) + } + .disposed(by: self.disposeBag) + + reactor.state.map(\.isLoading) + .distinctUntilChanged() + .subscribe(with: self.loadingIndicatorView) { loadingIndicatorView, isLoading in + if isLoading { + loadingIndicatorView.startAnimating() + } else { + loadingIndicatorView.stopAnimating() + } + } + .disposed(by: self.disposeBag) + + reactor.state.map(\.hasErrors) + .filter { $0 } + .subscribe(onNext: { _ in + + let actions: [SOMDialogAction] = [ + .init( + title: Text.inappositeDialogConfirmButtonTitle, + style: .primary, + action: { + UIApplication.topViewController?.dismiss(animated: true) + } + ) + ] + + SOMDialogViewController.show( + title: Text.inappositeDialogTitle, + message: Text.inappositeDialogMessage, + textAlignment: .left, + actions: actions + ) + }) + .disposed(by: self.disposeBag) + + reactor.state.map(\.profileImage) + .distinctUntilChanged() + .subscribe(with: self) { object, profileImage in + object.profileImageView.image = profileImage ?? .init(.image(.v2(.profile))) + + var actions: [SelectProfileBottomFloatView.FloatAction] = [ + .init( + title: Text.selectProfileFirstButtonTitle, + action: { [weak object] in + SwiftEntryKit.dismiss(.specific(entryName: Text.selectProfileEntryName)) { + object?.showPicker(for: .library) + } + } + ), + .init( + title: Text.selectProfileSecondButtonTitle, + action: { [weak object] in + SwiftEntryKit.dismiss(.specific(entryName: Text.selectProfileEntryName)) { + object?.showPicker(for: .photo) + } + } + ) + ] + + if profileImage != nil { + actions.append(.init( + title: Text.selectProfileThirdButtonTitle, + action: { + SwiftEntryKit.dismiss(.specific(entryName: Text.selectProfileEntryName)) { + reactor.action.onNext(.setDefaultImage) + } + } + )) + } + + object.actions = actions + } + .disposed(by: self.disposeBag) + } + } + +extension OnboardingProfileImageSettingViewController { + + func showPicker(for screen: YPPickerScreen) { + + var config = YPImagePickerConfiguration() + + config.library.options = nil + config.library.minWidthForItem = nil + config.showsCrop = .rectangle(ratio: 1.0) + config.showsPhotoFilters = false + config.library.preselectedItems = nil + config.screens = [screen] + config.startOnScreen = screen + config.shouldSaveNewPicturesToAlbum = false + + config.wordings.next = Text.selectPhotoFullScreenNextTitle + config.wordings.cancel = Text.selectPhotoFullScreenCancelTitle + config.wordings.save = Text.selectPhotoFullScreenSaveTitle + config.wordings.albumsTitle = Text.selectPhotoFullScreenAlbumsTitle + config.wordings.cameraTitle = Text.selectPhotoFullScreenCameraTitle + config.wordings.libraryTitle = Text.selectPhotoFullScreenLibraryTitle + config.wordings.crop = Text.selectPhotoFullScreenCropTitle + + let picker = YPImagePicker(configuration: config) + picker.didFinishPicking { [weak self, weak picker] items, cancelled in + + if cancelled { + Log.debug("Picker was canceled") + picker?.dismiss(animated: true, completion: nil) + return + } + + if let image = items.singlePhoto?.image, let reactor = self?.reactor { + reactor.action.onNext(.uploadImage(image)) + } else { + Log.error("Error occured while picking an image") + } + picker?.dismiss(animated: true, completion: nil) + } + + DispatchQueue.main.asyncAfter(deadline: .now() + 0.25) { + self.present(picker, animated: true, completion: nil) + } + } +} diff --git a/SOOUM/SOOUM/Presentations/Intro/Onboarding/ProfileImageSetting/OnboardingProfileImageSettingViewReactor.swift b/SOOUM/SOOUM/Presentations/Intro/Onboarding/ProfileImageSetting/OnboardingProfileImageSettingViewReactor.swift new file mode 100644 index 00000000..fca2b961 --- /dev/null +++ b/SOOUM/SOOUM/Presentations/Intro/Onboarding/ProfileImageSetting/OnboardingProfileImageSettingViewReactor.swift @@ -0,0 +1,153 @@ +// +// OnboardingProfileImageSettingViewReactor.swift +// SOOUM +// +// Created by JDeoks on 11/12/24. +// + +import ReactorKit + +import Alamofire + + +class OnboardingProfileImageSettingViewReactor: Reactor { + + enum Action { + case uploadImage(UIImage) + case setDefaultImage + case signUp + } + + enum Mutation { + case updateImageInfo(UIImage?, String?) + case updateIsSignUp(Bool) + case updateIsLoading(Bool) + case updateErrors(Bool) + } + + struct State { + var profileImage: UIImage? + var profileImageName: String? + var isSignUp: Bool + var isLoading: Bool + var hasErrors: Bool + } + + var initialState: State = .init( + profileImage: nil, + profileImageName: nil, + isSignUp: false, + isLoading: false, + hasErrors: false + ) + + private let dependencies: AppDIContainerable + private let userUseCase: UserUseCase + private let authUseCase: AuthUseCase + + private let nickname: String + + init(dependencies: AppDIContainerable, nickname: String) { + self.dependencies = dependencies + self.userUseCase = dependencies.rootContainer.resolve(UserUseCase.self) + self.authUseCase = dependencies.rootContainer.resolve(AuthUseCase.self) + self.nickname = nickname + } + + func mutate(action: Action) -> Observable { + switch action { + case let .uploadImage(image): + + return .concat([ + .just(.updateIsLoading(true)), + self.uploadImage(image), + .just(.updateIsLoading(false)) + ]) + case .setDefaultImage: + + return .just(.updateImageInfo(nil, nil)) + case .signUp: + + return self.signUp() + } + } + + func reduce(state: State, mutation: Mutation) -> State { + var newState = state + switch mutation { + case let .updateImageInfo(profileImage, profileImageName): + newState.profileImage = profileImage + newState.profileImageName = profileImageName + case let .updateIsSignUp(isSignUp): + newState.isSignUp = isSignUp + case let .updateIsLoading(isLoading): + newState.isLoading = isLoading + case let .updateErrors(hasErrors): + newState.hasErrors = hasErrors + } + return newState + } +} + +extension OnboardingProfileImageSettingViewReactor { + + private func signUp() -> Observable { + + return self.authUseCase.signUp( + nickname: self.nickname, + profileImageName: self.currentState.profileImageName + ) + .map(Mutation.updateIsSignUp) + } + + private func uploadImage(_ image: UIImage) -> Observable { + + return self.presignedURL() + .withUnretained(self) + .flatMapLatest { object, presignedInfo -> Observable in + if let imageData = image.jpegData(compressionQuality: 0.5), + let url = URL(string: presignedInfo.imgUrl) { + + return object.userUseCase.uploadImage(imageData, with: url) + .flatMapLatest { isSuccess -> Observable in + + let image = isSuccess ? image : nil + let imageName = isSuccess ? presignedInfo.imgName : nil + + return .just(.updateImageInfo(image, imageName)) + } + } else { + return .empty() + } + } + .delay(.milliseconds(1000), scheduler: MainScheduler.instance) + } + + private func presignedURL() -> Observable { + + return self.userUseCase.presignedURL() + } + + private var catchClosure: ((Error) throws -> Observable ) { + return { error in + + let nsError = error as NSError + let endProcessing = Observable.concat([ + .just(.updateImageInfo(nil, nil)), + .just(.updateIsSignUp(false)), + .just(.updateIsLoading(false)), + // 부적절한 이미지 업로드 에러 코드 == 402 + .just(.updateErrors(nsError.code == 402)) + ]) + + return endProcessing + } + } +} + +extension OnboardingProfileImageSettingViewReactor { + + func reactorForCompleted() -> OnboardingCompletedViewReactor { + OnboardingCompletedViewReactor(dependencies: self.dependencies) + } +} diff --git a/SOOUM/SOOUM/Presentations/Intro/Onboarding/ProfileImageSetting/ProfileImageSettingViewController.swift b/SOOUM/SOOUM/Presentations/Intro/Onboarding/ProfileImageSetting/ProfileImageSettingViewController.swift deleted file mode 100644 index 3c950576..00000000 --- a/SOOUM/SOOUM/Presentations/Intro/Onboarding/ProfileImageSetting/ProfileImageSettingViewController.swift +++ /dev/null @@ -1,203 +0,0 @@ -// -// ProfileImageSettingViewController.swift -// SOOUM -// -// Created by JDeoks on 11/6/24. -// - -import UIKit - -import ReactorKit -import RxCocoa -import RxGesture -import RxSwift - -import SnapKit -import Then -import YPImagePicker - -class ProfileImageSettingViewController: BaseNavigationViewController, View { - - enum Text { - static let title: String = "당신을 표현하는 사진을\n프로필로 등록해볼까요?" - static let message: String = "프로필 사진은 추후 변경이 가능해요" - - static let confirmButtonTitle: String = "확인" - static let passButtonTitle: String = "다음에 변경하기" - } - - - // MARK: Views - - private let guideMessageView = OnboardingGuideMessageView(title: Text.title, message: Text.message) - - private let profileImageView = UIImageView().then { - $0.image = .init(.image(.sooumLogo)) - $0.layer.cornerRadius = 128 * 0.5 - $0.clipsToBounds = true - } - private let cameraButton = SOMButton().then { - $0.image = .init(.icon(.outlined(.camera))) - $0.foregroundColor = .som.white - - $0.backgroundColor = .som.gray400 - $0.layer.cornerRadius = 32 * 0.5 - $0.clipsToBounds = true - } - - private let confirmButton = SOMButton().then { - $0.title = Text.confirmButtonTitle - $0.typography = .som.body1WithBold - $0.foregroundColor = .som.gray600 - - $0.backgroundColor = .som.gray300 - $0.layer.cornerRadius = 12 - $0.clipsToBounds = true - } - - private let passButton = SOMButton().then { - $0.title = Text.passButtonTitle - $0.typography = .som.body3WithBold - $0.foregroundColor = .som.p300 - $0.hasUnderlined = true - } - - - // MARK: Override func - - override func setupConstraints() { - super.setupConstraints() - - self.view.addSubview(self.guideMessageView) - self.guideMessageView.snp.makeConstraints { - $0.top.equalTo(view.safeAreaLayoutGuide.snp.top).offset(28) - $0.leading.equalToSuperview().offset(20) - $0.trailing.equalToSuperview().offset(-20) - } - - self.view.addSubview(self.profileImageView) - self.profileImageView.snp.makeConstraints { - $0.top.equalTo(self.guideMessageView.snp.bottom).offset(94) - $0.centerX.equalToSuperview() - $0.size.equalTo(128) - } - self.view.addSubview(self.cameraButton) - self.cameraButton.snp.makeConstraints { - $0.bottom.equalTo(self.profileImageView.snp.bottom).offset(-4) - $0.trailing.equalTo(self.profileImageView.snp.trailing).offset(-4) - $0.size.equalTo(32) - } - - self.view.addSubviews(self.passButton) - self.passButton.snp.makeConstraints { - $0.bottom.equalTo(self.view.safeAreaLayoutGuide.snp.bottom).offset(-12) - $0.centerX.equalToSuperview() - } - - self.view.addSubview(self.confirmButton) - self.confirmButton.snp.makeConstraints { - $0.bottom.equalTo(passButton.snp.top).offset(-20) - $0.leading.equalToSuperview().offset(20) - $0.trailing.equalToSuperview().offset(-20) - $0.height.equalTo(48) - } - } - - - // MARK: ReactorKit - bind - - func bind(reactor: ProfileImageSettingViewReactor) { - - // Action - self.cameraButton.rx.tap - .subscribe(with: self) { object, _ in - object.showPicker() - } - .disposed(by: self.disposeBag) - - self.confirmButton.rx.tap - .map { _ in Reactor.Action.updateProfile } - .bind(to: reactor.action) - .disposed(by: disposeBag) - - self.passButton.rx.tap - .map { _ in Reactor.Action.updateProfile } - .bind(to: reactor.action) - .disposed(by: disposeBag) - - // State - reactor.state.map(\.isSuccess) - .distinctUntilChanged() - .filter { $0 } - .subscribe(with: self) { object, _ in - let viewController = MainTabBarController() - viewController.reactor = reactor.reactorForMainTabBar() - let navigationController = UINavigationController( - rootViewController: viewController - ) - object.view.window?.rootViewController = navigationController - } - .disposed(by: disposeBag) - - reactor.state.map(\.profileImage) - .map { $0 != nil } - .distinctUntilChanged() - .subscribe(with: self) { object, isUpdated in - object.confirmButton.isEnabled = isUpdated - object.confirmButton.foregroundColor = isUpdated ? .som.white : .som.gray600 - object.confirmButton.backgroundColor = isUpdated ? .som.p300 : .som.gray300 - } - .disposed(by: disposeBag) - } - } - -extension ProfileImageSettingViewController { - func showPicker() { - var config = YPImagePickerConfiguration() - - config.library.options = nil - config.library.onlySquare = false - config.library.isSquareByDefault = true - config.library.minWidthForItem = nil - config.library.mediaType = YPlibraryMediaType.photo - config.library.defaultMultipleSelection = false - config.library.maxNumberOfItems = 1 - config.library.minNumberOfItems = 1 - config.library.numberOfItemsInRow = 4 - config.library.spacingBetweenItems = 1.0 - config.showsCrop = .rectangle(ratio: 1) - config.showsPhotoFilters = false - config.library.skipSelectionsGallery = false - config.library.preselectedItems = nil - config.library.preSelectItemOnMultipleSelection = true - config.startOnScreen = .library - config.shouldSaveNewPicturesToAlbum = false - - config.wordings.next = "다음" - config.wordings.cancel = "취소" - config.wordings.save = "저장" - config.wordings.albumsTitle = "앨범" - config.wordings.cameraTitle = "카메라" - config.wordings.libraryTitle = "갤러리" - config.wordings.crop = "자르기" - - let picker = YPImagePicker(configuration: config) - picker.didFinishPicking { [weak self] items, cancelled in - - guard let self = self, let reactor = self.reactor else { return } - - if cancelled { - Log.error("Picker was canceled") - picker.dismiss(animated: true, completion: nil) - return - } - - if let image = items.singlePhoto?.image { - self.profileImageView.image = image - reactor.action.onNext(.updateImage(image)) - } - picker.dismiss(animated: true, completion: nil) - } - self.present(picker, animated: true, completion: nil) - } -} diff --git a/SOOUM/SOOUM/Presentations/Intro/Onboarding/ProfileImageSetting/ProfileImageSettingViewReactor.swift b/SOOUM/SOOUM/Presentations/Intro/Onboarding/ProfileImageSetting/ProfileImageSettingViewReactor.swift deleted file mode 100644 index 27a48d0f..00000000 --- a/SOOUM/SOOUM/Presentations/Intro/Onboarding/ProfileImageSetting/ProfileImageSettingViewReactor.swift +++ /dev/null @@ -1,108 +0,0 @@ -// -// ProfileImageSettingViewReactor.swift -// SOOUM -// -// Created by JDeoks on 11/12/24. -// - -import ReactorKit - -import Alamofire - - -class ProfileImageSettingViewReactor: Reactor { - - enum Action { - case updateImage(UIImage) - case updateProfile - } - - enum Mutation { - case updateImage(UIImage) - case updateIsSuccess(Bool) - } - - struct State { - var profileImage: UIImage? - var isSuccess: Bool - } - - var nickname: String - var imageName: String? - - var initialState: State = .init( - profileImage: nil, - isSuccess: false - ) - - let provider: ManagerProviderType - - init(provider: ManagerProviderType, nickname: String) { - self.provider = provider - self.nickname = nickname - } - - func mutate(action: Action) -> Observable { - switch action { - case let .updateImage(image): - return self.updateImage(image) - case .updateProfile: - let trimedNickname = self.nickname.trimmingCharacters(in: .whitespacesAndNewlines) - let request: JoinRequest = .registerUser(userName: trimedNickname, imageName: self.imageName) - - return self.provider.networkManager.request(Empty.self, request: request) - .flatMapLatest { _ -> Observable in - return .just(.updateIsSuccess(true)) - } - } - } - - func reduce(state: State, mutation: Mutation) -> State { - var newState = state - switch mutation { - case let .updateImage(profileImage): - newState.profileImage = profileImage - case let .updateIsSuccess(isSuccess): - newState.isSuccess = isSuccess - } - return newState - } -} - -extension ProfileImageSettingViewReactor { - - private func updateImage(_ image: UIImage) -> Observable { - return self.presignedURL() - .withUnretained(self) - .flatMapLatest { object, presignedResponse -> Observable in - if let imageData = image.jpegData(compressionQuality: 0.5), - let url = URL(string: presignedResponse.strUrl) { - return object.provider.networkManager.upload(imageData, to: url) - .flatMapLatest { _ -> Observable in - return .just(.updateImage(image)) - } - } else { - return .empty() - } - } - } - - private func presignedURL() -> Observable<(strUrl: String, imageName: String)> { - let request: JoinRequest = .profileImagePresignedURL - - return self.provider.networkManager.request(PresignedStorageResponse.self, request: request) - .withUnretained(self) - .flatMapLatest { object, response -> Observable<(strUrl: String, imageName: String)> in - object.imageName = response.imgName - let result = (response.url.url, response.imgName) - return .just(result) - } - } -} - -extension ProfileImageSettingViewReactor { - - func reactorForMainTabBar() -> MainTabBarReactor { - MainTabBarReactor(provider: self.provider) - } -} diff --git a/SOOUM/SOOUM/Presentations/Intro/Onboarding/ProfileImageSetting/Views/SelectProfileBottomFloatView.swift b/SOOUM/SOOUM/Presentations/Intro/Onboarding/ProfileImageSetting/Views/SelectProfileBottomFloatView.swift new file mode 100644 index 00000000..119d61b3 --- /dev/null +++ b/SOOUM/SOOUM/Presentations/Intro/Onboarding/ProfileImageSetting/Views/SelectProfileBottomFloatView.swift @@ -0,0 +1,124 @@ +// +// SelectProfileBottomFloatView.swift +// SOOUM +// +// Created by 오현식 on 9/12/25. +// + +import UIKit + +import SnapKit +import Then + +class SelectProfileBottomFloatView: UIView { + + + // MARK: Views + + private let container = UIStackView().then { + $0.axis = .vertical + } + + + // MARK: Variables + + private var actions: [FloatAction]? + + + // MARK: Initalization + + convenience init(actions: [FloatAction]) { + self.init(frame: .zero) + + self.actions = actions + self.setupActions(actions) + } + + override init(frame: CGRect) { + super.init(frame: frame) + + self.setupConstraints() + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } +} + +private extension SelectProfileBottomFloatView { + + func setupConstraints() { + + self.snp.makeConstraints { + $0.width.equalTo(UIScreen.main.bounds.width - 16 * 2) + } + + let handleView = UIView().then { + $0.backgroundColor = .som.v2.gray300 + $0.layer.cornerRadius = 2 + } + self.addSubview(handleView) + handleView.snp.makeConstraints { + $0.top.equalToSuperview().offset(14) + $0.centerX.equalToSuperview() + $0.width.equalTo(44) + $0.height.equalTo(4) + } + + self.addSubview(self.container) + self.container.snp.makeConstraints { + $0.top.equalTo(handleView.snp.bottom).offset(14) + $0.bottom.equalToSuperview().offset(-16) + $0.leading.equalToSuperview().offset(16) + $0.trailing.equalToSuperview().offset(-16) + } + } + + func setupActions(_ actions: [FloatAction]) { + + self.container.arrangedSubviews.forEach { $0.removeFromSuperview() } + + actions.forEach { action in + + let button = SOMButton().then { + $0.title = action.title + $0.typography = .som.v2.subtitle1 + $0.foregroundColor = .som.v2.gray500 + $0.backgroundColor = .som.v2.white + $0.inset = .init(top: 12, left: 16, bottom: 12, right: 16) + + $0.contentHorizontalAlignment = .left + + $0.tag = action.tag + $0.addTarget(self, action: #selector(self.tap(_:)), for: .touchUpInside) + } + button.snp.makeConstraints { + $0.height.equalTo(48) + } + + self.container.addArrangedSubview(button) + } + } + + @objc + func tap(_ button: UIButton) { + if let action = self.actions?.first(where: { $0.tag == button.tag }) { + action.action() + } + } +} + +extension SelectProfileBottomFloatView { + + struct FloatAction { + let tag: Int + let title: String + let action: (() -> Void) + + init(title: String, action: @escaping (() -> Void)) { + self.tag = UUID().hashValue + self.title = title + self.action = action + } + } +} diff --git a/SOOUM/SOOUM/Presentations/Intro/Onboarding/TermsOfService/OnboardingTermsOfServiceViewController.swift b/SOOUM/SOOUM/Presentations/Intro/Onboarding/TermsOfService/OnboardingTermsOfServiceViewController.swift index 6513e206..a70c5b2d 100644 --- a/SOOUM/SOOUM/Presentations/Intro/Onboarding/TermsOfService/OnboardingTermsOfServiceViewController.swift +++ b/SOOUM/SOOUM/Presentations/Intro/Onboarding/TermsOfService/OnboardingTermsOfServiceViewController.swift @@ -19,15 +19,53 @@ import RxSwift class OnboardingTermsOfServiceViewController: BaseNavigationViewController, View { enum Text { - static let guideMessageTitle: String = "숨을 시작하기 위해서는\n약관 동의가 필요해요" + static let navigationTitle: String = "회원가입" - static let confirmButtonTitle: String = "확인" + static let guideMessageTitle: String = "숨 서비스 이용을 위해\n동의해주세요" + + static let termsOfSeviceUrlString: String = "https://mewing-space-6d3.notion.site/3f92380d536a4b569921d2809ed147ef?pvs=4" + static let locationServiceUrlString: String = "https://mewing-space-6d3.notion.site/45d151f68ba74b23b24483ad8b2662b4?pvs=4" + static let privacyPolicyUrlString: String = "https://mewing-space-6d3.notion.site/44e378c9d11d45159859492434b6b128?pvs=4" + + static let nextButtonTitle: String = "다음" + } + + enum TermsOfService: CaseIterable { + + case termsOfService + case locationService + case privacyPolicy + + var text: String { + switch self { + case .termsOfService: + "[필수] 서비스 이용 약관" + case .locationService: + "[필수] 위치정보 이용 약관" + case .privacyPolicy: + "[필수] 개인정보 처리 방침" + } + } + + var url: URL { + switch self { + case .termsOfService: + return URL(string: Text.termsOfSeviceUrlString)! + case .locationService: + return URL(string: Text.locationServiceUrlString)! + case .privacyPolicy: + return URL(string: Text.privacyPolicyUrlString)! + } + } } // MARK: Views - private let guideMessageView = OnboardingGuideMessageView(title: Text.guideMessageTitle) + private let guideMessageView = OnboardingGuideMessageView( + title: Text.guideMessageTitle, + currentNumber: 1 + ) private let agreeAllButtonView = TermsOfServiceAgreeButtonView() @@ -36,60 +74,66 @@ class OnboardingTermsOfServiceViewController: BaseNavigationViewController, View private let privacyPolicyCellView = TermsOfServiceCellView(title: TermsOfService.privacyPolicy.text) private let nextButton = SOMButton().then { - $0.title = Text.confirmButtonTitle - $0.typography = .som.body1WithBold - $0.foregroundColor = .som.gray600 - - $0.backgroundColor = .som.gray300 - $0.layer.cornerRadius = 12 - $0.clipsToBounds = true + $0.title = Text.nextButtonTitle + $0.typography = .som.v2.title1 + $0.foregroundColor = .som.v2.white + $0.backgroundColor = .som.v2.black + $0.isEnabled = false } // MARK: Override func + + override func setupNaviBar() { + super.setupNaviBar() + + self.navigationBar.title = Text.navigationTitle + } override func setupConstraints() { super.setupConstraints() self.view.addSubview(self.guideMessageView) self.guideMessageView.snp.makeConstraints { - $0.top.equalTo(self.view.safeAreaLayoutGuide.snp.top).offset(28) - $0.leading.equalToSuperview().offset(20) - $0.trailing.equalToSuperview().offset(-20) + $0.top.equalTo(self.view.safeAreaLayoutGuide.snp.top).offset(16) + $0.leading.equalToSuperview().offset(16) + $0.trailing.equalToSuperview().offset(-16) } self.view.addSubview(self.agreeAllButtonView) self.agreeAllButtonView.snp.makeConstraints { - $0.top.equalTo(self.guideMessageView.snp.bottom).offset(44) - $0.leading.equalToSuperview().offset(20) - $0.trailing.equalToSuperview().offset(-20) - $0.height.equalTo(60) + $0.top.equalTo(self.guideMessageView.snp.bottom).offset(32) + $0.leading.equalToSuperview() + $0.trailing.equalToSuperview() } self.view.addSubview(self.termsOfServiceCellView) self.termsOfServiceCellView.snp.makeConstraints { - $0.top.equalTo(self.agreeAllButtonView.snp.bottom).offset(36) - $0.leading.trailing.equalToSuperview() + $0.top.equalTo(self.agreeAllButtonView.snp.bottom).offset(8) + $0.leading.equalToSuperview() + $0.trailing.equalToSuperview() } self.view.addSubview(self.locationServiceCellView) self.locationServiceCellView.snp.makeConstraints { $0.top.equalTo(self.termsOfServiceCellView.snp.bottom) - $0.leading.trailing.equalToSuperview() + $0.leading.equalToSuperview() + $0.trailing.equalToSuperview() } self.view.addSubview(self.privacyPolicyCellView) self.privacyPolicyCellView.snp.makeConstraints { $0.top.equalTo(self.locationServiceCellView.snp.bottom) - $0.leading.trailing.equalToSuperview() + $0.leading.equalToSuperview() + $0.trailing.equalToSuperview() } self.view.addSubview(self.nextButton) self.nextButton.snp.makeConstraints { - $0.bottom.equalTo(self.view.safeAreaLayoutGuide).offset(-12) - $0.leading.equalToSuperview().offset(20) - $0.trailing.equalToSuperview().offset(-20) - $0.height.equalTo(48) + $0.bottom.equalTo(self.view.safeAreaLayoutGuide.snp.bottom) + $0.leading.equalToSuperview().offset(16) + $0.trailing.equalToSuperview().offset(-16) + $0.height.equalTo(56) } } @@ -99,8 +143,7 @@ class OnboardingTermsOfServiceViewController: BaseNavigationViewController, View func bind(reactor: OnboardingTermsOfServiceViewReactor) { // Action - self.agreeAllButtonView.rx.tapGesture() - .when(.recognized) + self.agreeAllButtonView.rx.didSelect .map { _ in Reactor.Action.allAgree } .bind(to: reactor.action) .disposed(by: disposeBag) @@ -110,7 +153,7 @@ class OnboardingTermsOfServiceViewController: BaseNavigationViewController, View .bind(to: reactor.action) .disposed(by: self.disposeBag) - self.termsOfServiceCellView.rx.nextSelect + self.termsOfServiceCellView.rx.moveSelect .subscribe(onNext: { _ in if UIApplication.shared.canOpenURL(TermsOfService.termsOfService.url) { UIApplication.shared.open( @@ -127,7 +170,7 @@ class OnboardingTermsOfServiceViewController: BaseNavigationViewController, View .bind(to: reactor.action) .disposed(by: self.disposeBag) - self.locationServiceCellView.rx.nextSelect + self.locationServiceCellView.rx.moveSelect .subscribe(onNext: { _ in if UIApplication.shared.canOpenURL(TermsOfService.locationService.url) { UIApplication.shared.open( @@ -144,7 +187,7 @@ class OnboardingTermsOfServiceViewController: BaseNavigationViewController, View .bind(to: reactor.action) .disposed(by: self.disposeBag) - self.privacyPolicyCellView.rx.nextSelect + self.privacyPolicyCellView.rx.moveSelect .subscribe(onNext: { _ in if UIApplication.shared.canOpenURL(TermsOfService.privacyPolicy.url) { UIApplication.shared.open( @@ -157,13 +200,6 @@ class OnboardingTermsOfServiceViewController: BaseNavigationViewController, View .disposed(by: self.disposeBag) self.nextButton.rx.tap - .map { _ in Reactor.Action.signUp } - .bind(to: reactor.action) - .disposed(by: disposeBag) - - // State - reactor.state.map(\.shouldNavigate) - .filter { $0 } .subscribe(with: self) { object, _ in let nicknameSettingVC = OnboardingNicknameSettingViewController() nicknameSettingVC.reactor = reactor.reactorForNickname() @@ -175,10 +211,7 @@ class OnboardingTermsOfServiceViewController: BaseNavigationViewController, View reactor.state.map(\.isAllAgreed) .distinctUntilChanged() .subscribe(with: self) { object, isAllAgreed in - object.agreeAllButtonView.updateState(isAllAgreed) - - object.nextButton.foregroundColor = isAllAgreed ? .som.white : .som.gray600 - object.nextButton.backgroundColor = isAllAgreed ? .som.p300 : .som.gray300 + object.agreeAllButtonView.updateState(isAllAgreed, animated: false) object.nextButton.isEnabled = isAllAgreed } .disposed(by: self.disposeBag) @@ -186,21 +219,21 @@ class OnboardingTermsOfServiceViewController: BaseNavigationViewController, View reactor.state.map(\.isTermsOfServiceAgreed) .distinctUntilChanged() .subscribe(with: self) { object, isTermsOfServiceAgreed in - object.termsOfServiceCellView.updateState(isTermsOfServiceAgreed) + object.termsOfServiceCellView.updateState(isTermsOfServiceAgreed, animated: false) } .disposed(by: self.disposeBag) reactor.state.map(\.isLocationAgreed) .distinctUntilChanged() .subscribe(with: self) { object, isLocationAgreed in - object.locationServiceCellView.updateState(isLocationAgreed) + object.locationServiceCellView.updateState(isLocationAgreed, animated: false) } .disposed(by: self.disposeBag) reactor.state.map(\.isPrivacyPolicyAgreed) .distinctUntilChanged() .subscribe(with: self) { object, isPrivacyPolicyAgreed in - object.privacyPolicyCellView.updateState(isPrivacyPolicyAgreed) + object.privacyPolicyCellView.updateState(isPrivacyPolicyAgreed, animated: false) } .disposed(by: self.disposeBag) } diff --git a/SOOUM/SOOUM/Presentations/Intro/Onboarding/TermsOfService/OnboardingTermsOfServiceViewReactor.swift b/SOOUM/SOOUM/Presentations/Intro/Onboarding/TermsOfService/OnboardingTermsOfServiceViewReactor.swift index 7e4af8ff..27c92972 100644 --- a/SOOUM/SOOUM/Presentations/Intro/Onboarding/TermsOfService/OnboardingTermsOfServiceViewReactor.swift +++ b/SOOUM/SOOUM/Presentations/Intro/Onboarding/TermsOfService/OnboardingTermsOfServiceViewReactor.swift @@ -10,47 +10,9 @@ import UIKit import ReactorKit import RxSwift - -enum TermsOfService: CaseIterable { - - enum Text { - static let termsOfSeviceUrlString: String = "https://mewing-space-6d3.notion.site/3f92380d536a4b569921d2809ed147ef?pvs=4" - static let locationServiceUrlString: String = "https://mewing-space-6d3.notion.site/45d151f68ba74b23b24483ad8b2662b4?pvs=4" - static let privacyPolicyUrlString: String = "https://mewing-space-6d3.notion.site/44e378c9d11d45159859492434b6b128?pvs=4" - } - - case termsOfService - case locationService - case privacyPolicy - - var text: String { - switch self { - case .termsOfService: - "[필수] 서비스 이용 약관" - case .locationService: - "[필수] 위치정보 이용 약관" - case .privacyPolicy: - "[필수] 개인정보 처리 방침" - } - } - - var url: URL { - switch self { - case .termsOfService: - return URL(string: Text.termsOfSeviceUrlString)! - case .locationService: - return URL(string: Text.locationServiceUrlString)! - case .privacyPolicy: - return URL(string: Text.privacyPolicyUrlString)! - } - } -} - class OnboardingTermsOfServiceViewReactor: Reactor { enum Action { - /// 약관동의 전 회원가입 - case signUp /// 모두 동의 버튼 클릭 case allAgree /// 이용약관 버튼 클릭 @@ -62,25 +24,21 @@ class OnboardingTermsOfServiceViewReactor: Reactor { } enum Mutation { - /// 약관동의 전 가입 api 결과 - case signUpResult(Bool) /// 이용약관 설정 - case setIsTermsOfServiceAgreed(Bool) + case updateIsTermsOfServiceAgreed(Bool) /// 위치 동의 설정 - case setIsLocationAgreed(Bool) + case updateIsLocationAgreed(Bool) /// 개인정보 동의 설정 - case setIsPrivacyPolicyAgreed(Bool) + case updateIsPrivacyPolicyAgreed(Bool) } struct State { - /// 다음 화면으로 넘어가기 필요 여부 - fileprivate(set) var shouldNavigate: Bool = false /// 이용약관 동의 여부 - fileprivate(set) var isTermsOfServiceAgreed = false + fileprivate(set) var isTermsOfServiceAgreed: Bool /// 위치 동의 여부 - fileprivate(set) var isLocationAgreed = false + fileprivate(set) var isLocationAgreed: Bool /// 개인정보 처리 동의 여부 - fileprivate(set) var isPrivacyPolicyAgreed = false + fileprivate(set) var isPrivacyPolicyAgreed: Bool /// 전체동의 여부 var isAllAgreed: Bool { @@ -89,35 +47,38 @@ class OnboardingTermsOfServiceViewReactor: Reactor { && self.isPrivacyPolicyAgreed } } - var initialState = State() + var initialState = State( + isTermsOfServiceAgreed: false, + isLocationAgreed: false, + isPrivacyPolicyAgreed: false + ) - let provider: ManagerProviderType + private let dependencies: AppDIContainerable + private let authUseCase: AuthUseCase - init(provider: ManagerProviderType) { - self.provider = provider + init(dependencies: AppDIContainerable) { + self.dependencies = dependencies + self.authUseCase = dependencies.rootContainer.resolve(AuthUseCase.self) } func mutate(action: Action) -> Observable { switch action { - case .signUp: - return self.provider.authManager.join() - .map(Mutation.signUpResult) - case .termsOfServiceAgree: - return .just(.setIsTermsOfServiceAgreed(!self.currentState.isTermsOfServiceAgreed)) + return .just(.updateIsTermsOfServiceAgreed(!self.currentState.isTermsOfServiceAgreed)) case .locationAgree: - return .just(.setIsLocationAgreed(!self.currentState.isLocationAgreed)) + return .just(.updateIsLocationAgreed(!self.currentState.isLocationAgreed)) case .privacyPolicyAgree: - return .just(.setIsPrivacyPolicyAgreed(!self.currentState.isPrivacyPolicyAgreed)) + return .just(.updateIsPrivacyPolicyAgreed(!self.currentState.isPrivacyPolicyAgreed)) case .allAgree: + let isAgreed: Bool = !self.currentState.isAllAgreed return .concat([ - .just(.setIsTermsOfServiceAgreed(isAgreed)), - .just(.setIsLocationAgreed(isAgreed)), - .just(.setIsPrivacyPolicyAgreed(isAgreed)) + .just(.updateIsTermsOfServiceAgreed(isAgreed)), + .just(.updateIsLocationAgreed(isAgreed)), + .just(.updateIsPrivacyPolicyAgreed(isAgreed)) ]) } } @@ -125,16 +86,11 @@ class OnboardingTermsOfServiceViewReactor: Reactor { func reduce(state: State, mutation: Mutation) -> State { var newState = state switch mutation { - case let .signUpResult(shouldNavigate): - newState.shouldNavigate = shouldNavigate - - case let .setIsTermsOfServiceAgreed(isAgreed): + case let .updateIsTermsOfServiceAgreed(isAgreed): newState.isTermsOfServiceAgreed = isAgreed - - case let .setIsLocationAgreed(isAgreed): + case let .updateIsLocationAgreed(isAgreed): newState.isLocationAgreed = isAgreed - - case let .setIsPrivacyPolicyAgreed(isAgreed): + case let .updateIsPrivacyPolicyAgreed(isAgreed): newState.isPrivacyPolicyAgreed = isAgreed } return newState @@ -144,6 +100,6 @@ class OnboardingTermsOfServiceViewReactor: Reactor { extension OnboardingTermsOfServiceViewReactor { func reactorForNickname() -> OnboardingNicknameSettingViewReactor { - OnboardingNicknameSettingViewReactor(provider: self.provider) + OnboardingNicknameSettingViewReactor(dependencies: self.dependencies) } } diff --git a/SOOUM/SOOUM/Presentations/Intro/Onboarding/TermsOfService/Views/TermsOfServiceAgreeButtonView+Rx.swift b/SOOUM/SOOUM/Presentations/Intro/Onboarding/TermsOfService/Views/TermsOfServiceAgreeButtonView+Rx.swift new file mode 100644 index 00000000..de386b69 --- /dev/null +++ b/SOOUM/SOOUM/Presentations/Intro/Onboarding/TermsOfService/Views/TermsOfServiceAgreeButtonView+Rx.swift @@ -0,0 +1,17 @@ +// +// TermsOfServiceAgreeButtonView+Rx.swift +// SOOUM +// +// Created by 오현식 on 9/11/25. +// + +import RxCocoa +import RxSwift + + +extension Reactive where Base: TermsOfServiceAgreeButtonView { + + var didSelect: ControlEvent { + self.base.backgroundButton.rx.tap + } +} diff --git a/SOOUM/SOOUM/Presentations/Intro/Onboarding/TermsOfService/Views/TermsOfServiceAgreeButtonView.swift b/SOOUM/SOOUM/Presentations/Intro/Onboarding/TermsOfService/Views/TermsOfServiceAgreeButtonView.swift index 2b2a0c57..45e95c14 100644 --- a/SOOUM/SOOUM/Presentations/Intro/Onboarding/TermsOfService/Views/TermsOfServiceAgreeButtonView.swift +++ b/SOOUM/SOOUM/Presentations/Intro/Onboarding/TermsOfService/Views/TermsOfServiceAgreeButtonView.swift @@ -14,7 +14,7 @@ import Then class TermsOfServiceAgreeButtonView: UIView { enum Text { - static let title: String = "약관 전체 동의" + static let title: String = "전체 동의하기" } @@ -22,13 +22,17 @@ class TermsOfServiceAgreeButtonView: UIView { private let checkImageView = UIImageView().then { $0.image = .init(.icon(.outlined(.check))) - $0.tintColor = .som.gray600 + $0.tintColor = .som.v2.gray400 } private let titleLabel = UILabel().then { $0.text = Text.title - $0.textColor = .som.gray600 - $0.typography = .som.head2WithRegular + $0.textColor = .som.v2.gray600 + $0.typography = .som.v2.title1 + } + + let backgroundButton = SOMButton().then { + $0.backgroundColor = .som.v2.gray100 } @@ -49,21 +53,31 @@ class TermsOfServiceAgreeButtonView: UIView { private func setupConstraints() { - self.layer.borderColor = UIColor.som.gray300.cgColor - self.layer.borderWidth = 1 - self.layer.cornerRadius = 12 + let container = UIView() + self.addSubview(container) + container.snp.makeConstraints { + $0.verticalEdges.equalToSuperview() + $0.leading.equalToSuperview().offset(16) + $0.trailing.equalToSuperview().offset(-16) + $0.height.equalTo(56) + } - self.addSubview(self.checkImageView) + container.addSubview(self.backgroundButton) + self.backgroundButton.snp.makeConstraints { + $0.edges.equalToSuperview() + } + + container.addSubview(self.checkImageView) self.checkImageView.snp.makeConstraints { $0.centerY.equalToSuperview() - $0.leading.equalToSuperview().offset(16) + $0.leading.equalToSuperview().offset(24) $0.size.equalTo(24) } - self.addSubview(self.titleLabel) + container.addSubview(self.titleLabel) self.titleLabel.snp.makeConstraints { $0.centerY.equalToSuperview() - $0.leading.equalTo(self.checkImageView.snp.trailing).offset(6) + $0.leading.equalTo(self.checkImageView.snp.trailing).offset(8) $0.trailing.lessThanOrEqualToSuperview() } } @@ -76,9 +90,7 @@ class TermsOfServiceAgreeButtonView: UIView { let animationDuration: TimeInterval = animated ? 0.25 : 0 UIView.animate(withDuration: animationDuration) { - self.layer.borderColor = state ? UIColor.som.p300.cgColor : UIColor.som.gray300.cgColor - self.checkImageView.tintColor = state ? .som.p300 : .som.gray600 - self.titleLabel.textColor = state ? .som.p300 : .som.gray600 + self.checkImageView.tintColor = state ? .som.v2.pDark : .som.v2.gray400 } } } diff --git a/SOOUM/SOOUM/Presentations/Intro/Onboarding/TermsOfService/Views/TermsOfServiceCellView+Rx.swift b/SOOUM/SOOUM/Presentations/Intro/Onboarding/TermsOfService/Views/TermsOfServiceCellView+Rx.swift index 585cee2b..87ad549c 100644 --- a/SOOUM/SOOUM/Presentations/Intro/Onboarding/TermsOfService/Views/TermsOfServiceCellView+Rx.swift +++ b/SOOUM/SOOUM/Presentations/Intro/Onboarding/TermsOfService/Views/TermsOfServiceCellView+Rx.swift @@ -15,7 +15,7 @@ extension Reactive where Base: TermsOfServiceCellView { self.base.backgroundButton.rx.tap } - var nextSelect: ControlEvent { - self.base.nextButton.rx.tap + var moveSelect: ControlEvent { + self.base.moveButton.rx.tap } } diff --git a/SOOUM/SOOUM/Presentations/Intro/Onboarding/TermsOfService/Views/TermsOfServiceCellView.swift b/SOOUM/SOOUM/Presentations/Intro/Onboarding/TermsOfService/Views/TermsOfServiceCellView.swift index b8bcc958..80fc4849 100644 --- a/SOOUM/SOOUM/Presentations/Intro/Onboarding/TermsOfService/Views/TermsOfServiceCellView.swift +++ b/SOOUM/SOOUM/Presentations/Intro/Onboarding/TermsOfService/Views/TermsOfServiceCellView.swift @@ -17,21 +17,23 @@ class TermsOfServiceCellView: UIView { // MARK: Views private let checkBoxImageView = UIImageView().then { - $0.image = .init(.icon(.outlined(.checkBox))) - $0.tintColor = .som.gray500 + $0.image = .init(.icon(.v2(.outlined(.check)))) + $0.tintColor = .som.v2.gray200 } private let titleLabel = UILabel().then { - $0.textColor = .som.gray500 - $0.typography = .som.body1WithRegular + $0.textColor = .som.v2.gray500 + $0.typography = .som.v2.subtitle1 } - let nextButton = SOMButton().then { - $0.image = .init(.icon(.outlined(.next))) - $0.foregroundColor = .som.gray800 + let moveButton = SOMButton().then { + $0.image = .init(.icon(.v2(.outlined(.right)))) + $0.foregroundColor = .som.v2.gray500 } - let backgroundButton = UIButton() + let backgroundButton = SOMButton().then { + $0.backgroundColor = .som.v2.white + } // MARK: Initalization @@ -57,37 +59,38 @@ class TermsOfServiceCellView: UIView { private func setupConstraints() { - self.snp.makeConstraints { - $0.width.equalTo(UIScreen.main.bounds.width) - $0.height.equalTo(44) + let container = UIView() + self.addSubview(container) + container.snp.makeConstraints { + $0.verticalEdges.equalToSuperview() + $0.leading.equalToSuperview().offset(16) + $0.trailing.equalToSuperview().offset(-16) + $0.height.equalTo(48) + } + + container.addSubview(self.backgroundButton) + self.backgroundButton.snp.makeConstraints { + $0.edges.equalToSuperview() } - self.addSubview(self.checkBoxImageView) + container.addSubview(self.checkBoxImageView) self.checkBoxImageView.snp.makeConstraints { $0.centerY.equalToSuperview() - $0.leading.equalToSuperview().offset(36) + $0.leading.equalToSuperview().offset(24) $0.size.equalTo(24) } - self.addSubview(self.titleLabel) + container.addSubview(self.titleLabel) self.titleLabel.snp.makeConstraints { $0.centerY.equalToSuperview() - $0.leading.equalTo(self.checkBoxImageView.snp.trailing).offset(6) + $0.leading.equalTo(self.checkBoxImageView.snp.trailing).offset(8) } - self.addSubview(self.backgroundButton) - self.backgroundButton.snp.makeConstraints { - $0.top.equalTo(self.checkBoxImageView.snp.top) - $0.bottom.equalTo(self.checkBoxImageView.snp.bottom) - $0.leading.equalTo(self.checkBoxImageView.snp.leading) - $0.trailing.equalTo(self.titleLabel.snp.trailing) - } - - self.addSubview(self.nextButton) - self.nextButton.snp.makeConstraints { - $0.centerY.trailing.equalToSuperview() - $0.leading.equalTo(self.titleLabel.snp.trailing).offset(6) - $0.trailing.equalToSuperview().offset(-30) + container.addSubview(self.moveButton) + self.moveButton.snp.makeConstraints { + $0.centerY.equalToSuperview() + $0.leading.greaterThanOrEqualTo(self.titleLabel.snp.trailing).offset(12) + $0.trailing.equalToSuperview().offset(-20) $0.size.equalTo(32) } } @@ -100,12 +103,7 @@ class TermsOfServiceCellView: UIView { let animationDuration: TimeInterval = animated ? 0.25 : 0 UIView.animate(withDuration: animationDuration) { - self.checkBoxImageView.image = state ? .init(.icon(.filled(.checkBox))) : .init(.icon(.outlined(.checkBox))) - self.checkBoxImageView.tintColor = state ? .som.p300 : .som.gray600 - - self.titleLabel.textColor = state ? .som.p300 : .som.gray600 - - self.nextButton.foregroundColor = state ? .som.p300 : .som.gray600 + self.checkBoxImageView.tintColor = state ? .som.v2.pDark : .som.v2.gray200 } } } diff --git a/SOOUM/SOOUM/Presentations/Intro/Onboarding/Views/OnboardingGuideMessageView.swift b/SOOUM/SOOUM/Presentations/Intro/Onboarding/Views/OnboardingGuideMessageView.swift index 9367ccdf..343c5d86 100644 --- a/SOOUM/SOOUM/Presentations/Intro/Onboarding/Views/OnboardingGuideMessageView.swift +++ b/SOOUM/SOOUM/Presentations/Intro/Onboarding/Views/OnboardingGuideMessageView.swift @@ -21,19 +21,16 @@ class OnboardingGuideMessageView: UIView { // MARK: Views + private let numberingView = OnboardingNumberingView(numbers: [1, 2, 3]) + private let titleLabel = UILabel().then { - $0.textColor = .som.black - $0.typography = .som.head1WithRegular.withAlignment(.left) + $0.textColor = .som.v2.black + $0.typography = .som.v2.head2 $0.lineBreakMode = .byWordWrapping $0.lineBreakStrategy = .hangulWordPriority $0.numberOfLines = 0 } - private let messageLabel = UILabel().then { - $0.textColor = .som.gray500 - $0.typography = .som.body2WithRegular - } - // MARK: Variables @@ -46,24 +43,23 @@ class OnboardingGuideMessageView: UIView { } } - var message: String? { + var currentNumber: Int { set { - self.messageLabel.text = newValue - self.messageLabel.isHidden = newValue == nil + self.numberingView.currentNumber = newValue } get { - return self.messageLabel.text + self.numberingView.currentNumber ?? 0 } } // MARK: Initalization - convenience init(title: String, message: String? = nil) { + convenience init(title: String, currentNumber: Int) { self.init(frame: .zero) self.title = title - self.message = message + self.currentNumber = currentNumber } override init(frame: CGRect) { @@ -81,15 +77,15 @@ class OnboardingGuideMessageView: UIView { private func setupConstraints() { - self.addSubview(self.titleLabel) - self.titleLabel.snp.makeConstraints { + self.addSubview(self.numberingView) + self.numberingView.snp.makeConstraints { $0.top.leading.equalToSuperview() $0.trailing.lessThanOrEqualToSuperview() } - self.addSubview(self.messageLabel) - self.messageLabel.snp.makeConstraints { - $0.top.equalTo(self.titleLabel.snp.bottom).offset(16) + self.addSubview(self.titleLabel) + self.titleLabel.snp.makeConstraints { + $0.top.equalTo(self.numberingView.snp.bottom).offset(16) $0.bottom.leading.equalToSuperview() $0.trailing.lessThanOrEqualToSuperview() } diff --git a/SOOUM/SOOUM/Presentations/Intro/Onboarding/Views/OnboardingNumberingView.swift b/SOOUM/SOOUM/Presentations/Intro/Onboarding/Views/OnboardingNumberingView.swift new file mode 100644 index 00000000..b323d0f7 --- /dev/null +++ b/SOOUM/SOOUM/Presentations/Intro/Onboarding/Views/OnboardingNumberingView.swift @@ -0,0 +1,104 @@ +// +// OnboardingNumberingView.swift +// SOOUM +// +// Created by 오현식 on 9/11/25. +// + +import UIKit + +import SnapKit +import Then + +class OnboardingNumberingView: UIView { + + enum Color { + static let selectedBackgroundColor: UIColor = .som.v2.pMain + static let selectedBorderColor: UIColor = .som.v2.pLight2 + static let defaultBackgroundColor: UIColor = .som.v2.gray300 + static let defaultBorderColor: UIColor = .som.v2.gray200 + } + + + // MARK: Views + + private let container = UIStackView().then { + $0.axis = .horizontal + $0.spacing = 8 + } + + + // MARK: Variables + + var currentNumber: Int? { + willSet { + self.container.subviews.forEach { view in + if view.tag <= newValue ?? 1 { + view.backgroundColor = Color.selectedBackgroundColor + view.layer.borderColor = Color.selectedBorderColor.cgColor + } + } + } + } + + + // MARK: Initalization + + convenience init(numbers: [Int]) { + self.init(frame: .zero) + + self.setupNumberView(numbers: numbers) + } + + override init(frame: CGRect) { + super.init(frame: frame) + + self.setupConstraints() + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } +} + +private extension OnboardingNumberingView { + + func setupConstraints() { + + self.addSubview(self.container) + self.container.snp.makeConstraints { + $0.edges.equalToSuperview() + } + } + + func setupNumberView(numbers: [Int]) { + + numbers.forEach { number in + + let backgroundView = UIView().then { + $0.backgroundColor = Color.defaultBackgroundColor + $0.layer.borderColor = Color.defaultBorderColor.cgColor + $0.layer.borderWidth = 1 + $0.layer.cornerRadius = 32 * 0.5 + $0.tag = number + } + + let label = UILabel().then { + $0.text = "\(number)" + $0.textColor = .white + $0.typography = .som.v2.subtitle2 + } + + backgroundView.addSubview(label) + label.snp.makeConstraints { + $0.center.equalToSuperview() + } + + backgroundView.snp.makeConstraints { + $0.size.equalTo(32) + } + + self.container.addArrangedSubview(backgroundView) + } + } +} diff --git a/SOOUM/SOOUM/Presentations/Main/Home/Detail/Cells/DetailViewCell.swift b/SOOUM/SOOUM/Presentations/Main/Home/Detail/Cells/DetailViewCell.swift index 6101394a..2ba393e9 100644 --- a/SOOUM/SOOUM/Presentations/Main/Home/Detail/Cells/DetailViewCell.swift +++ b/SOOUM/SOOUM/Presentations/Main/Home/Detail/Cells/DetailViewCell.swift @@ -114,7 +114,7 @@ class DetailViewCell: UICollectionViewCell { if let strUrl = self.member.profileImgUrl?.url { self.memberImageView.setImage(strUrl: strUrl) } else { - self.memberImageView.image = .init(.image(.sooumLogo)) + self.memberImageView.image = .init(.image(.defaultStyle(.sooumLogo))) } self.memberLabel.text = self.member.nickname } diff --git a/SOOUM/SOOUM/Presentations/Main/Home/MainHomeTabBarController.swift b/SOOUM/SOOUM/Presentations/Main/Home/MainHomeTabBarController.swift index 43292642..73ad9e7e 100644 --- a/SOOUM/SOOUM/Presentations/Main/Home/MainHomeTabBarController.swift +++ b/SOOUM/SOOUM/Presentations/Main/Home/MainHomeTabBarController.swift @@ -33,7 +33,7 @@ class MainHomeTabBarController: BaseNavigationViewController, View { // MARK: Set navigationBar Items private let logo = UIImageView().then { - $0.image = .init(.logo) + $0.image = .init(.logo(.logo)) $0.tintColor = .som.p300 $0.contentMode = .scaleAspectFit } @@ -147,9 +147,9 @@ class MainHomeTabBarController: BaseNavigationViewController, View { if let windowScene: UIWindowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene, let window: UIWindow = windowScene.windows.first(where: { $0.isKeyWindow }) { - let viewController = OnboardingViewController() - viewController.reactor = OnboardingViewReactor(provider: object.reactor!.provider) - window.rootViewController = UINavigationController(rootViewController: viewController) + // let viewController = OnboardingViewController() + // viewController.reactor = OnboardingViewReactor(provider: object.reactor!.provider) + // window.rootViewController = UINavigationController(rootViewController: viewController) } } } diff --git a/SOOUM/SOOUM/Presentations/Main/Home/MainHomeTabBarReactor.swift b/SOOUM/SOOUM/Presentations/Main/Home/MainHomeTabBarReactor.swift index b33e3343..e6910fa1 100644 --- a/SOOUM/SOOUM/Presentations/Main/Home/MainHomeTabBarReactor.swift +++ b/SOOUM/SOOUM/Presentations/Main/Home/MainHomeTabBarReactor.swift @@ -42,11 +42,12 @@ class MainHomeTabBarReactor: Reactor { func mutate(action: Action) -> Observable { switch action { case .notisWithoutRead: - let request: NotificationRequest = .totalWithoutReadCount - - return self.provider.networkManager.request(WithoutReadNotisCountResponse.self, request: request) - .map { $0.unreadCnt == "0" } - .map(Mutation.notisWithoutRead) + // let request: NotificationRequest = .totalWithoutReadCount + // + // return self.provider.networkManager.request(WithoutReadNotisCountResponse.self, request: request) + // .map { $0.unreadCnt == "0" } + // .map(Mutation.notisWithoutRead) + return .empty() case let .requestRead(selectedId): let request: NotificationRequest = .requestRead(notificationId: selectedId) return self.provider.networkManager.request(Empty.self, request: request) diff --git a/SOOUM/SOOUM/Presentations/Main/Home/Notification/Views/NotificationViewReactor.swift b/SOOUM/SOOUM/Presentations/Main/Home/Notification/Views/NotificationViewReactor.swift index d1eae60f..9d36d54a 100644 --- a/SOOUM/SOOUM/Presentations/Main/Home/Notification/Views/NotificationViewReactor.swift +++ b/SOOUM/SOOUM/Presentations/Main/Home/Notification/Views/NotificationViewReactor.swift @@ -67,48 +67,49 @@ class NotificationViewReactor: Reactor { switch action { case .landing: - let combined = Observable.concat([ - self.withoutReadNotisCount(), - self.notifications(with: false), - self.notifications(with: true) - ]) - .delay(.milliseconds(500), scheduler: MainScheduler.instance) - - return .concat([ - .just(.updateIsProcessing(true)), - combined, - .just(.updateIsProcessing(false)) - ]) - +// let combined = Observable.concat([ +// self.withoutReadNotisCount(), +// self.notifications(with: false), +// self.notifications(with: true) +// ]) +// .delay(.milliseconds(500), scheduler: MainScheduler.instance) +// +// return .concat([ +// .just(.updateIsProcessing(true)), +// combined, +// .just(.updateIsProcessing(false)) +// ]) + return .empty() case .refresh: - let combined = Observable.concat([ - self.withoutReadNotisCount(), - self.notifications(with: false), - self.notifications(with: true) - ]) - .delay(.milliseconds(500), scheduler: MainScheduler.instance) - - return .concat([ - .just(.updateIsLoading(true)), - combined, - .just(.updateIsLoading(false)) - ]) - +// let combined = Observable.concat([ +// self.withoutReadNotisCount(), +// self.notifications(with: false), +// self.notifications(with: true) +// ]) +// .delay(.milliseconds(500), scheduler: MainScheduler.instance) +// +// return .concat([ +// .just(.updateIsLoading(true)), +// combined, +// .just(.updateIsLoading(false)) +// ]) + return .empty() case let .moreFind(withoutReadLastId, readLastId): - let combined = Observable.concat([ - self.withoutReadNotisCount(), - self.moreNotifications(with: false, lastId: withoutReadLastId), - self.moreNotifications(with: true, lastId: readLastId) - ]) - .delay(.milliseconds(500), scheduler: MainScheduler.instance) - - return .concat([ - .just(.updateIsProcessing(true)), - combined, - .just(.updateIsProcessing(false)) - ]) +// let combined = Observable.concat([ +// self.withoutReadNotisCount(), +// self.moreNotifications(with: false, lastId: withoutReadLastId), +// self.moreNotifications(with: true, lastId: readLastId) +// ]) +// .delay(.milliseconds(500), scheduler: MainScheduler.instance) +// +// return .concat([ +// .just(.updateIsProcessing(true)), +// combined, +// .just(.updateIsProcessing(false)) +// ]) + return .empty() case let .requestRead(selectedId): self.provider.pushManager.deleteNotification(notificationId: selectedId) @@ -142,71 +143,71 @@ class NotificationViewReactor: Reactor { } } -extension NotificationViewReactor { - - private func notifications(with isRead: Bool) -> Observable { - - var request: NotificationRequest { - switch self.entranceType { - case .total: - return isRead ? .totalRead(lastId: nil) : .totalWithoutRead(lastId: nil) - case .comment: - return isRead ? .commentRead(lastId: nil) : .commentWithoutRead(lastId: nil) - case .like: - return isRead ? .likeRead(lastId: nil) : .likeWithoutRead(lastId: nil) - } - } - - return self.provider.networkManager.request(CommentHistoryInNotiResponse.self, request: request) - .map(\.commentHistoryInNotis) - .map(isRead ? Mutation.notifications : Mutation.notificationsWithoutRead) - .catch(self.catchClosure) - } - - private func moreNotifications(with isRead: Bool, lastId: String?) -> Observable { - - guard let lastId = lastId else { return .just(.more([])) } - - var request: NotificationRequest { - switch self.entranceType { - case .total: - return isRead ? .totalRead(lastId: lastId) : .totalWithoutRead(lastId: lastId) - case .comment: - return isRead ? .commentRead(lastId: lastId) : .commentWithoutRead(lastId: lastId) - case .like: - return isRead ? .likeRead(lastId: lastId) : .likeWithoutRead(lastId: lastId) - } - } - - return self.provider.networkManager.request(CommentHistoryInNotiResponse.self, request: request) - .map(\.commentHistoryInNotis) - .map(isRead ? Mutation.more : Mutation.moreWithoutRead) - .catch(self.catchClosure) - } - - private func withoutReadNotisCount() -> Observable { - - var request: NotificationRequest { - switch self.entranceType { - case .total: - return .totalWithoutReadCount - case .comment: - return .commentWithoutReadCount - case .like: - return .likeWihoutReadCount - } - } - - return self.provider.networkManager.request(WithoutReadNotisCountResponse.self, request: request) - .map(\.unreadCnt) - .map(Mutation.withoutReadNotiscount) - } - - var catchClosure: ((Error) throws -> Observable ) { - return { _ in - .concat([ - .just(.updateIsProcessing(false)) - ]) - } - } -} +//extension NotificationViewReactor { +// +// private func notifications(with isRead: Bool) -> Observable { +// +// var request: NotificationRequest { +// switch self.entranceType { +// case .total: +// return isRead ? .totalRead(lastId: nil) : .totalWithoutRead(lastId: nil) +// case .comment: +// return isRead ? .commentRead(lastId: nil) : .commentWithoutRead(lastId: nil) +// case .like: +// return isRead ? .likeRead(lastId: nil) : .likeWithoutRead(lastId: nil) +// } +// } +// +// return self.provider.networkManager.request(CommentHistoryInNotiResponse.self, request: request) +// .map(\.commentHistoryInNotis) +// .map(isRead ? Mutation.notifications : Mutation.notificationsWithoutRead) +// .catch(self.catchClosure) +// } +// +// private func moreNotifications(with isRead: Bool, lastId: String?) -> Observable { +// +// guard let lastId = lastId else { return .just(.more([])) } +// +// var request: NotificationRequest { +// switch self.entranceType { +// case .total: +// return isRead ? .totalRead(lastId: lastId) : .totalWithoutRead(lastId: lastId) +// case .comment: +// return isRead ? .commentRead(lastId: lastId) : .commentWithoutRead(lastId: lastId) +// case .like: +// return isRead ? .likeRead(lastId: lastId) : .likeWithoutRead(lastId: lastId) +// } +// } +// +// return self.provider.networkManager.request(CommentHistoryInNotiResponse.self, request: request) +// .map(\.commentHistoryInNotis) +// .map(isRead ? Mutation.more : Mutation.moreWithoutRead) +// .catch(self.catchClosure) +// } +// +// private func withoutReadNotisCount() -> Observable { +// +// var request: NotificationRequest { +// switch self.entranceType { +// case .total: +// return .totalWithoutReadCount +// case .comment: +// return .commentWithoutReadCount +// case .like: +// return .likeWihoutReadCount +// } +// } +// +// return self.provider.networkManager.request(WithoutReadNotisCountResponse.self, request: request) +// .map(\.unreadCnt) +// .map(Mutation.withoutReadNotiscount) +// } +// +// var catchClosure: ((Error) throws -> Observable ) { +// return { _ in +// .concat([ +// .just(.updateIsProcessing(false)) +// ]) +// } +// } +//} diff --git a/SOOUM/SOOUM/Presentations/Main/Profile/Cells/MyProfileViewCell.swift b/SOOUM/SOOUM/Presentations/Main/Profile/Cells/MyProfileViewCell.swift index 7b9a371e..a5d4a688 100644 --- a/SOOUM/SOOUM/Presentations/Main/Profile/Cells/MyProfileViewCell.swift +++ b/SOOUM/SOOUM/Presentations/Main/Profile/Cells/MyProfileViewCell.swift @@ -180,7 +180,7 @@ class MyProfileViewCell: UICollectionViewCell { if let profileImg = profile.profileImg { self.profileImageView.setImage(strUrl: profileImg.url) } else { - self.profileImageView.image = .init(.image(.sooumLogo)) + self.profileImageView.image = .init(.image(.defaultStyle(.sooumLogo))) } self.totalCardCountLabel.text = profile.cardCnt self.totalFollowingCountLabel.text = profile.followingCnt diff --git a/SOOUM/SOOUM/Presentations/Main/Profile/Cells/OtherProfileViewCell.swift b/SOOUM/SOOUM/Presentations/Main/Profile/Cells/OtherProfileViewCell.swift index 857701fb..6db75487 100644 --- a/SOOUM/SOOUM/Presentations/Main/Profile/Cells/OtherProfileViewCell.swift +++ b/SOOUM/SOOUM/Presentations/Main/Profile/Cells/OtherProfileViewCell.swift @@ -185,7 +185,7 @@ class OtherProfileViewCell: UICollectionViewCell { if let profileImg = profile.profileImg { self.profileImageView.setImage(strUrl: profileImg.url) } else { - self.profileImageView.image = .init(.image(.sooumLogo)) + self.profileImageView.image = .init(.image(.defaultStyle(.sooumLogo))) } self.totalCardCountLabel.text = profile.cardCnt self.totalFollowingCountLabel.text = profile.followingCnt diff --git a/SOOUM/SOOUM/Presentations/Main/Profile/Follow/Cells/MyFollowerViewCell.swift b/SOOUM/SOOUM/Presentations/Main/Profile/Follow/Cells/MyFollowerViewCell.swift index 4cc85bce..72ddae61 100644 --- a/SOOUM/SOOUM/Presentations/Main/Profile/Follow/Cells/MyFollowerViewCell.swift +++ b/SOOUM/SOOUM/Presentations/Main/Profile/Follow/Cells/MyFollowerViewCell.swift @@ -120,7 +120,7 @@ class MyFollowerViewCell: UITableViewCell { if let url = follow.backgroundImgURL?.url { self.profileImageView.setImage(strUrl: url) } else { - self.profileImageView.image = .init(.image(.sooumLogo)) + self.profileImageView.image = .init(.image(.defaultStyle(.sooumLogo))) } self.profileNickname.text = follow.nickname } diff --git a/SOOUM/SOOUM/Presentations/Main/Profile/Follow/Cells/MyFollowingViewCell.swift b/SOOUM/SOOUM/Presentations/Main/Profile/Follow/Cells/MyFollowingViewCell.swift index 43188c17..d674de2e6 100644 --- a/SOOUM/SOOUM/Presentations/Main/Profile/Follow/Cells/MyFollowingViewCell.swift +++ b/SOOUM/SOOUM/Presentations/Main/Profile/Follow/Cells/MyFollowingViewCell.swift @@ -134,7 +134,7 @@ class MyFollowingViewCell: UITableViewCell { if let url = follow.backgroundImgURL?.url { self.profileImageView.setImage(strUrl: url) } else { - self.profileImageView.image = .init(.image(.sooumLogo)) + self.profileImageView.image = .init(.image(.defaultStyle(.sooumLogo))) } self.profileNickname.text = follow.nickname } diff --git a/SOOUM/SOOUM/Presentations/Main/Profile/Follow/Cells/OtherFollowViewCell.swift b/SOOUM/SOOUM/Presentations/Main/Profile/Follow/Cells/OtherFollowViewCell.swift index f36f62ba..7c32871d 100644 --- a/SOOUM/SOOUM/Presentations/Main/Profile/Follow/Cells/OtherFollowViewCell.swift +++ b/SOOUM/SOOUM/Presentations/Main/Profile/Follow/Cells/OtherFollowViewCell.swift @@ -120,7 +120,7 @@ class OtherFollowViewCell: UITableViewCell { if let url = follow.backgroundImgURL?.url { self.profileImageView.setImage(strUrl: url) } else { - self.profileImageView.image = .init(.image(.sooumLogo)) + self.profileImageView.image = .init(.image(.defaultStyle(.sooumLogo))) } self.profileNickname.text = follow.nickname diff --git a/SOOUM/SOOUM/Presentations/Main/Profile/Settings/MemberTransfer/Enter/EnterMemberTransferViewController.swift b/SOOUM/SOOUM/Presentations/Main/Profile/Settings/MemberTransfer/Enter/EnterMemberTransferViewController.swift index 210baf36..1b5d5ac1 100644 --- a/SOOUM/SOOUM/Presentations/Main/Profile/Settings/MemberTransfer/Enter/EnterMemberTransferViewController.swift +++ b/SOOUM/SOOUM/Presentations/Main/Profile/Settings/MemberTransfer/Enter/EnterMemberTransferViewController.swift @@ -18,132 +18,141 @@ import RxSwift class EnterMemberTransferViewController: BaseNavigationViewController, View { enum Text { - static let navigationTitle: String = "계정 이관 코드 입력" - static let transferEnterMessage: String = "발급받은 코드를 입력해주세요" - static let transferEnterButtonTitle: String = "계정 이관하기" - } - - private let transferEnterMessageLabel = UILabel().then { - $0.text = Text.transferEnterMessage - $0.textColor = .som.gray800 - $0.typography = .som.body1WithBold - } - - private lazy var textFieldBackgroundView = UIView().then { - $0.backgroundColor = .som.gray50 - $0.layer.borderColor = UIColor.som.p300.cgColor - $0.layer.borderWidth = 2 - $0.layer.cornerRadius = 12 - - let gestureRecognizer = UITapGestureRecognizer( - target: self, - action: #selector(self.touch) - ) - $0.addGestureRecognizer(gestureRecognizer) - } - private lazy var textField = UITextField().then { - let paragraphStyle = NSMutableParagraphStyle() - $0.defaultTextAttributes[.paragraphStyle] = paragraphStyle - $0.defaultTextAttributes[.foregroundColor] = UIColor.som.black - $0.defaultTextAttributes[.font] = Typography.som.body1WithRegular.font - $0.tintColor = .som.p300 + static let navigationTitle: String = "내 계정 가져오기" - $0.textAlignment = .center + static let title: String = "기존 계정이 있으신가요?" - $0.enablesReturnKeyAutomatically = true - $0.returnKeyType = .go + static let placeholderText: String = "코드 입력" + static let textfieldGuideMessage: String = "코드는 발급 후 24시간 동안 유효해요" - $0.autocapitalizationType = .none - $0.autocorrectionType = .no - $0.spellCheckingType = .no + static let guideTitle: String = "내 계정 가져오기 안내" + static let guideMessage: String = "기존 휴대폰의 숨 앱 [설정>내 계정 내보내기]에서 발급한 코드를 입력하면, 기존 계정을 현재 휴대폰에서 그대로 사용할 수 있어요" - $0.setContentHuggingPriority(.defaultLow, for: .horizontal) - $0.setContentCompressionResistancePriority(.defaultHigh + 1, for: .vertical) + static let dialogTitle: String = "유효하지 않은 코드예요" + static let dialogMessage: String = "코드를 확인한 뒤 다시 시도해주세요." + static let dialogConfirmButtonTitle: String = "확인" - $0.delegate = self + static let confirmButtonTitle: String = "확인" } - private let transferMemberButton = SOMButton().then { - $0.title = Text.transferEnterButtonTitle - $0.typography = .som.body1WithBold - $0.foregroundColor = .som.white - - $0.backgroundColor = .som.p300 - $0.layer.cornerRadius = 12 - $0.clipsToBounds = true + private let titleLabel = UILabel().then { + $0.text = Text.title + $0.textColor = .som.v2.black + $0.typography = .som.v2.head2 } + private let transferTextField = EnterMemberTransferTextFieldView().then { + $0.placeholder = Text.placeholderText + $0.guideMessage = Text.textfieldGuideMessage + } - // MARK: Override func + private let container = UIStackView().then { + $0.axis = .vertical + $0.spacing = 16 + } - @objc - private func touch(sender: UIGestureRecognizer) { - if !self.textField.isFirstResponder { - self.textField.becomeFirstResponder() - } + private let confirmButton = SOMButton().then { + $0.title = Text.confirmButtonTitle + $0.typography = .som.v2.title1 + $0.foregroundColor = .som.v2.white + $0.backgroundColor = .som.v2.black + $0.isEnabled = false } + + // MARK: Override func + override func setupNaviBar() { super.setupNaviBar() self.navigationBar.title = Text.navigationTitle } - override func viewWillAppear(_ animated: Bool) { - super.viewWillAppear(animated) - - self.textField.becomeFirstResponder() - } - override func setupConstraints() { super.setupConstraints() - let transferBackgroundView = UIView().then { - $0.backgroundColor = .som.gray50 - $0.layer.cornerRadius = 22 - $0.clipsToBounds = true - } - self.view.addSubview(transferBackgroundView) - transferBackgroundView.snp.makeConstraints { - $0.top.equalTo(self.view.safeAreaLayoutGuide.snp.top).offset(149) - $0.leading.equalToSuperview().offset(20) - $0.trailing.equalToSuperview().offset(-20) + self.view.addSubview(self.titleLabel) + self.titleLabel.snp.makeConstraints { + $0.top.equalTo(self.view.safeAreaLayoutGuide.snp.top).offset(16) + $0.leading.equalToSuperview().offset(16) + $0.trailing.lessThanOrEqualToSuperview().offset(-16) } - transferBackgroundView.addSubview(self.transferEnterMessageLabel) - self.transferEnterMessageLabel.snp.makeConstraints { - $0.top.equalToSuperview().offset(44) - $0.centerX.equalToSuperview() + self.view.addSubview(self.transferTextField) + self.transferTextField.snp.makeConstraints { + $0.top.equalTo(self.titleLabel.snp.bottom).offset(32) + $0.leading.trailing.equalToSuperview() } - transferBackgroundView.addSubview(self.textFieldBackgroundView) - self.textFieldBackgroundView.snp.makeConstraints { - $0.top.equalTo(self.transferEnterMessageLabel.snp.bottom).offset(46) - $0.bottom.trailing.equalToSuperview().offset(-20) - $0.leading.equalToSuperview().offset(20) - $0.height.equalTo(64) + let guideTitleView = UIView() + let guideTitleImageView = UIImageView().then { + $0.image = .init(.icon(.v2(.filled(.info)))) + $0.tintColor = .som.v2.black + } + let guideTitleLabel = UILabel().then { + $0.text = Text.guideTitle + $0.textColor = .som.v2.black + $0.typography = .som.v2.subtitle2 } - self.textFieldBackgroundView.addSubview(self.textField) - self.textField.snp.makeConstraints { + guideTitleView.addSubview(guideTitleImageView) + guideTitleImageView.snp.makeConstraints { $0.centerY.equalToSuperview() - $0.leading.equalToSuperview().offset(20) - $0.trailing.equalToSuperview().offset(-20) + $0.leading.equalToSuperview() + $0.size.equalTo(16) + } + guideTitleView.addSubview(guideTitleLabel) + guideTitleLabel.snp.makeConstraints { + $0.top.bottom.equalToSuperview() + $0.leading.equalTo(guideTitleImageView.snp.trailing).offset(6) + $0.trailing.lessThanOrEqualToSuperview() + } + + let guideMessageLabel = UILabel().then { + $0.text = Text.guideMessage + $0.textColor = .som.v2.gray500 + $0.typography = .som.v2.caption2 + $0.numberOfLines = 0 + $0.lineBreakMode = .byCharWrapping } - self.view.addSubview(self.transferMemberButton) - self.transferMemberButton.snp.makeConstraints { - $0.bottom.equalTo(self.view.safeAreaLayoutGuide.snp.bottom).offset(-12) - $0.leading.equalToSuperview().offset(20) - $0.trailing.equalToSuperview().offset(-20) - $0.height.equalTo(48) + let guideView = UIView().then { + $0.backgroundColor = .som.v2.pLight1 + $0.layer.cornerRadius = 10 + } + guideView.addSubview(guideTitleView) + guideTitleView.snp.makeConstraints { + $0.top.equalToSuperview().offset(14) + $0.leading.equalToSuperview().offset(16) + $0.trailing.equalToSuperview().offset(-16) + } + + guideView.addSubview(guideMessageLabel) + guideMessageLabel.snp.makeConstraints { + $0.top.equalTo(guideTitleView.snp.bottom).offset(4) + $0.bottom.equalToSuperview().offset(-14) + $0.leading.equalToSuperview().offset(16) + $0.trailing.equalToSuperview().offset(-16) + } + self.container.addArrangedSubview(guideView) + + self.confirmButton.snp.makeConstraints { + $0.height.equalTo(56) + } + self.container.addArrangedSubview(self.confirmButton) + + self.view.addSubview(self.container) + self.container.snp.makeConstraints { + $0.bottom.equalTo(self.view.safeAreaLayoutGuide.snp.bottom) + $0.leading.equalToSuperview().offset(16) + $0.trailing.equalToSuperview().offset(-16) } } override func updatedKeyboard(withoutBottomSafeInset height: CGFloat) { super.updatedKeyboard(withoutBottomSafeInset: height) - let margin: CGFloat = height + 24 - self.transferMemberButton.snp.updateConstraints { + let margin: CGFloat = height + 12 + self.container.snp.updateConstraints { $0.bottom.equalTo(self.view.safeAreaLayoutGuide.snp.bottom).offset(-margin) } } @@ -154,18 +163,13 @@ class EnterMemberTransferViewController: BaseNavigationViewController, View { func bind(reactor: EnterMemberTransferViewReactor) { // Action - let transferCode = self.textField.rx.text.orEmpty.distinctUntilChanged() + let transferCode = self.transferTextField.rx.text.orEmpty.distinctUntilChanged() transferCode - .map { $0.isEmpty } - .subscribe(with: self) { object, isEmpty in - - object.transferMemberButton.isEnabled = isEmpty == false - object.transferMemberButton.foregroundColor = isEmpty ? .som.gray600 : .som.white - object.transferMemberButton.backgroundColor = isEmpty ? .som.gray300 : .som.p300 - } + .map { $0.isEmpty == false } + .bind(to: self.confirmButton.rx.isEnabled) .disposed(by: self.disposeBag) - self.transferMemberButton.rx.throttleTap(.seconds(1)) + self.confirmButton.rx.throttleTap .withLatestFrom(transferCode) .map(Reactor.Action.enterTransferCode) .bind(to: reactor.action) @@ -177,8 +181,9 @@ class EnterMemberTransferViewController: BaseNavigationViewController, View { .bind(to: self.activityIndicatorView.rx.isAnimating) .disposed(by: self.disposeBag) - reactor.state.map(\.isSuccess) - .distinctUntilChanged() + let isSuccess = reactor.state.map(\.isSuccess).distinctUntilChanged().share() + + isSuccess .filter { $0 } .subscribe(with: self) { object, _ in let launchScreenViewController = LaunchScreenViewController() @@ -186,6 +191,26 @@ class EnterMemberTransferViewController: BaseNavigationViewController, View { object.view.window?.rootViewController = launchScreenViewController } .disposed(by: self.disposeBag) + + isSuccess + .filter { $0 == false } + .subscribe(onNext: { _ in + let confirmAction = SOMDialogAction( + title: Text.dialogConfirmButtonTitle, + style: .primary, + action: { + UIApplication.topViewController?.dismiss(animated: true) + } + ) + + SOMDialogViewController.show( + title: Text.dialogTitle, + message: Text.dialogMessage, + textAlignment: .left, + actions: [confirmAction] + ) + }) + .disposed(by: self.disposeBag) } } diff --git a/SOOUM/SOOUM/Presentations/Main/Profile/Settings/MemberTransfer/Enter/EnterMemberTransferViewReactor.swift b/SOOUM/SOOUM/Presentations/Main/Profile/Settings/MemberTransfer/Enter/EnterMemberTransferViewReactor.swift index d399ce7e..8ed9a40f 100644 --- a/SOOUM/SOOUM/Presentations/Main/Profile/Settings/MemberTransfer/Enter/EnterMemberTransferViewReactor.swift +++ b/SOOUM/SOOUM/Presentations/Main/Profile/Settings/MemberTransfer/Enter/EnterMemberTransferViewReactor.swift @@ -34,10 +34,11 @@ class EnterMemberTransferViewReactor: Reactor { var initialState: State - let provider: ManagerProviderType + private let dependencies: AppDIContainerable - init(provider: ManagerProviderType, entranceType: EntranceType) { - self.provider = provider + init(dependencies: AppDIContainerable, entranceType: EntranceType) { + self.dependencies = dependencies + self.initialState = .init( isSuccess: false, isProcessing: false, @@ -49,37 +50,38 @@ class EnterMemberTransferViewReactor: Reactor { switch action { case let .enterTransferCode(transferCode): - return .concat([ - .just(.updateIsProcessing(true)), - - self.provider.networkManager.request(RSAKeyResponse.self, request: AuthRequest.getPublicKey) - .map(\.publicKey) - .withUnretained(self) - .flatMapLatest { object, publicKey -> Observable in - - if let secKey = object.provider.authManager.convertPEMToSecKey(pemString: publicKey), - let encryptedDeviceId = object.provider.authManager.encryptUUIDWithPublicKey(publicKey: secKey) { - - let request: SettingsRequest = .transferMember( - transferId: transferCode, - encryptedDeviceId: encryptedDeviceId - ) - - return self.provider.networkManager.request(Status.self, request: request) - .withUnretained(self) - .flatMapLatest { object, response -> Observable in - object.provider.authManager.initializeAuthInfo() - - return .just(.enterTransferCode(response.httpCode != 400)) - } - } else { - return .just(.enterTransferCode(false)) - } - } - .catch(self.catchClosure), - - .just(.updateIsProcessing(false)) - ]) +// return .concat([ +// .just(.updateIsProcessing(true)), +// +// self.provider.networkManager.request(RSAKeyResponse.self, request: AuthRequest.getPublicKey) +// .map(\.publicKey) +// .withUnretained(self) +// .flatMapLatest { object, publicKey -> Observable in +// +// if let secKey = object.provider.authManager.convertPEMToSecKey(pemString: publicKey), +// let encryptedDeviceId = object.provider.authManager.encryptUUIDWithPublicKey(publicKey: secKey) { +// +// let request: SettingsRequest = .transferMember( +// transferId: transferCode, +// encryptedDeviceId: encryptedDeviceId +// ) +// +// return self.provider.networkManager.request(Status.self, request: request) +// .withUnretained(self) +// .flatMapLatest { object, response -> Observable in +// object.provider.authManager.initializeAuthInfo() +// +// return .just(.enterTransferCode(response.httpCode != 400)) +// } +// } else { +// return .just(.enterTransferCode(false)) +// } +// } +// .catch(self.catchClosure), +// +// .just(.updateIsProcessing(false)) +// ]) + return .empty() } } @@ -110,6 +112,6 @@ extension EnterMemberTransferViewReactor { extension EnterMemberTransferViewReactor { func reactorForLaunch() -> LaunchScreenViewReactor { - LaunchScreenViewReactor(provider: self.provider) + LaunchScreenViewReactor(dependencies: self.dependencies, pushInfo: nil) } } diff --git a/SOOUM/SOOUM/Presentations/Main/Profile/Settings/MemberTransfer/Enter/Views/EnterMemberTransferTextFieldView+Rx.swift b/SOOUM/SOOUM/Presentations/Main/Profile/Settings/MemberTransfer/Enter/Views/EnterMemberTransferTextFieldView+Rx.swift new file mode 100644 index 00000000..07a846bc --- /dev/null +++ b/SOOUM/SOOUM/Presentations/Main/Profile/Settings/MemberTransfer/Enter/Views/EnterMemberTransferTextFieldView+Rx.swift @@ -0,0 +1,19 @@ +// +// EnterMemberTransferTextFieldView+Rx.swift +// SOOUM +// +// Created by 오현식 on 9/12/25. +// + +import UIKit + +import RxCocoa +import RxSwift + + +extension Reactive where Base: EnterMemberTransferTextFieldView { + + var text: ControlProperty { + self.base.textField.rx.text + } +} diff --git a/SOOUM/SOOUM/Presentations/Main/Profile/Settings/MemberTransfer/Enter/Views/EnterMemberTransferTextFieldView.swift b/SOOUM/SOOUM/Presentations/Main/Profile/Settings/MemberTransfer/Enter/Views/EnterMemberTransferTextFieldView.swift new file mode 100644 index 00000000..7f31ec98 --- /dev/null +++ b/SOOUM/SOOUM/Presentations/Main/Profile/Settings/MemberTransfer/Enter/Views/EnterMemberTransferTextFieldView.swift @@ -0,0 +1,175 @@ +// +// EnterMemberTransferTextFieldView.swift +// SOOUM +// +// Created by 오현식 on 9/12/25. +// + +import UIKit + +import SnapKit +import Then + + +class EnterMemberTransferTextFieldView: UIView { + + + // MARK: Views + + private lazy var textFieldBackgroundView = UIView().then { + $0.backgroundColor = .som.v2.gray100 + $0.layer.cornerRadius = 10 + + let gestureRecognizer = UITapGestureRecognizer( + target: self, + action: #selector(self.touch) + ) + $0.addGestureRecognizer(gestureRecognizer) + } + + lazy var textField = UITextField().then { + let paragraphStyle = NSMutableParagraphStyle() + $0.defaultTextAttributes[.paragraphStyle] = paragraphStyle + $0.defaultTextAttributes[.foregroundColor] = UIColor.som.v2.black + $0.defaultTextAttributes[.font] = Typography.som.v2.subtitle1.font + $0.tintColor = UIColor.som.v2.black + + $0.enablesReturnKeyAutomatically = true + $0.returnKeyType = .go + + $0.autocapitalizationType = .none + $0.autocorrectionType = .no + $0.spellCheckingType = .no + + $0.setContentHuggingPriority(.defaultLow, for: .horizontal) + $0.setContentCompressionResistancePriority(.defaultHigh + 1, for: .vertical) + + $0.delegate = self + } + + private let guideMessageLabel = UILabel().then { + $0.textColor = .som.v2.gray500 + $0.typography = .som.v2.caption2 + } + + + // MARK: Variables + + var text: String? { + set { + self.textField.text = newValue + } + get { + return self.textField.text + } + } + + var placeholder: String? { + set { + if let string: String = newValue { + self.textField.attributedPlaceholder = NSAttributedString( + string: string, + attributes: [ + .foregroundColor: UIColor.som.v2.gray500, + .font: Typography.som.v2.subtitle1.font + ] + ) + } else { + self.textField.attributedPlaceholder = nil + } + } + + get { + return self.textField.attributedPlaceholder?.string + } + } + + var guideMessage: String? { + set { + self.guideMessageLabel.text = newValue + } + get { + return self.guideMessageLabel.text + } + } + + var isTextEmpty: Bool { + return self.text?.isEmpty ?? false + } + + + // MARK: Initalization + + override init(frame: CGRect) { + super.init(frame: frame) + + self.setupConstraints() + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + + // MARK: Override func + + override var isFirstResponder: Bool { + return self.textField.isFirstResponder + } + + @discardableResult + override func becomeFirstResponder() -> Bool { + return self.textField.becomeFirstResponder() + } + + @discardableResult + override func resignFirstResponder() -> Bool { + return self.textField.resignFirstResponder() + } + + + // MARK: Objc func + + @objc + private func touch(sender: UIGestureRecognizer) { + if !self.textField.isFirstResponder { + self.textField.becomeFirstResponder() + } + } + + + // MARK: Private func + + private func setupConstraints() { + + self.addSubview(self.textFieldBackgroundView) + self.textFieldBackgroundView.snp.makeConstraints { + $0.top.equalToSuperview() + $0.leading.equalToSuperview().offset(16) + $0.trailing.equalToSuperview().offset(-16) + $0.height.equalTo(54) + } + self.textFieldBackgroundView.addSubview(self.textField) + self.textField.snp.makeConstraints { + $0.centerY.equalToSuperview() + $0.leading.equalToSuperview().offset(24) + $0.trailing.equalToSuperview().offset(-20) + } + + self.addSubview(self.guideMessageLabel) + self.guideMessageLabel.snp.makeConstraints { + $0.top.equalTo(self.textFieldBackgroundView.snp.bottom).offset(8) + $0.bottom.equalToSuperview() + $0.leading.equalToSuperview().offset(16) + $0.trailing.lessThanOrEqualToSuperview().offset(-16) + } + } +} + +extension EnterMemberTransferTextFieldView: UITextFieldDelegate { + + func textFieldShouldReturn(_ textField: UITextField) -> Bool { + textField.resignFirstResponder() + return true + } +} diff --git a/SOOUM/SOOUM/Presentations/Main/Profile/Settings/Resign/ResignViewController.swift b/SOOUM/SOOUM/Presentations/Main/Profile/Settings/Resign/ResignViewController.swift index 89fccf68..74fed458 100644 --- a/SOOUM/SOOUM/Presentations/Main/Profile/Settings/Resign/ResignViewController.swift +++ b/SOOUM/SOOUM/Presentations/Main/Profile/Settings/Resign/ResignViewController.swift @@ -225,12 +225,12 @@ class ResignViewController: BaseNavigationViewController, View { .subscribe(with: self) { object, _ in guard let window = object.view.window else { return } - let onboardingViewController = OnboardingViewController() - onboardingViewController.reactor = reactor.reactorForOnboarding() - onboardingViewController.modalTransitionStyle = .crossDissolve + // let onboardingViewController = OnboardingViewController() + // onboardingViewController.reactor = reactor.reactorForOnboarding() + // onboardingViewController.modalTransitionStyle = .crossDissolve - let navigationViewController = UINavigationController(rootViewController: onboardingViewController) - window.rootViewController = navigationViewController + // let navigationViewController = UINavigationController(rootViewController: onboardingViewController) + // window.rootViewController = navigationViewController object.navigationController?.viewControllers = [] } diff --git a/SOOUM/SOOUM/Presentations/Main/Profile/Settings/Resign/ResignViewReactor.swift b/SOOUM/SOOUM/Presentations/Main/Profile/Settings/Resign/ResignViewReactor.swift index 3d6b05ef..ca8055a3 100644 --- a/SOOUM/SOOUM/Presentations/Main/Profile/Settings/Resign/ResignViewReactor.swift +++ b/SOOUM/SOOUM/Presentations/Main/Profile/Settings/Resign/ResignViewReactor.swift @@ -99,9 +99,9 @@ class ResignViewReactor: Reactor { extension ResignViewReactor { - func reactorForOnboarding() -> OnboardingViewReactor { - OnboardingViewReactor(provider: self.provider) - } + // func reactorForOnboarding() -> OnboardingViewReactor { + // OnboardingViewReactor(provider: self.provider) + // } } extension ResignViewReactor { diff --git a/SOOUM/SOOUM/Presentations/Main/Profile/Settings/SettingsViewController.swift b/SOOUM/SOOUM/Presentations/Main/Profile/Settings/SettingsViewController.swift index e35afd21..cd04f959 100644 --- a/SOOUM/SOOUM/Presentations/Main/Profile/Settings/SettingsViewController.swift +++ b/SOOUM/SOOUM/Presentations/Main/Profile/Settings/SettingsViewController.swift @@ -208,9 +208,9 @@ class SettingsViewController: BaseNavigationViewController, View { self.enterUserTransferCodeCellView.rx.didSelect .subscribe(with: self) { object, _ in - let enterMemberTransferViewController = EnterMemberTransferViewController() - enterMemberTransferViewController.reactor = reactor.reactorForTransferEnter() - object.navigationPush(enterMemberTransferViewController, animated: true, bottomBarHidden: true) + // let enterMemberTransferViewController = EnterMemberTransferViewController() + // enterMemberTransferViewController.reactor = reactor.reactorForTransferEnter() + // object.navigationPush(enterMemberTransferViewController, animated: true, bottomBarHidden: true) } .disposed(by: self.disposeBag) diff --git a/SOOUM/SOOUM/Presentations/Main/Profile/Settings/SettingsViewReactor.swift b/SOOUM/SOOUM/Presentations/Main/Profile/Settings/SettingsViewReactor.swift index 8d56b94a..c1ab4775 100644 --- a/SOOUM/SOOUM/Presentations/Main/Profile/Settings/SettingsViewReactor.swift +++ b/SOOUM/SOOUM/Presentations/Main/Profile/Settings/SettingsViewReactor.swift @@ -115,9 +115,9 @@ extension SettingsViewReactor { IssueMemberTransferViewReactor(provider: self.provider) } - func reactorForTransferEnter() -> EnterMemberTransferViewReactor { - EnterMemberTransferViewReactor(provider: self.provider, entranceType: .settings) - } + // func reactorForTransferEnter() -> EnterMemberTransferViewReactor { + // EnterMemberTransferViewReactor(provider: self.provider, entranceType: .settings) + // } func reactorForResign() -> ResignViewReactor { ResignViewReactor(provider: self.provider, banEndAt: self.currentState.banEndAt) diff --git a/SOOUM/SOOUM/Presentations/Main/Profile/UpdateProfile/UpdateProfileViewController.swift b/SOOUM/SOOUM/Presentations/Main/Profile/UpdateProfile/UpdateProfileViewController.swift index 64b0ee05..a7577505 100644 --- a/SOOUM/SOOUM/Presentations/Main/Profile/UpdateProfile/UpdateProfileViewController.swift +++ b/SOOUM/SOOUM/Presentations/Main/Profile/UpdateProfile/UpdateProfileViewController.swift @@ -93,7 +93,7 @@ class UpdateProfileViewController: BaseNavigationViewController, View { func bind(reactor: UpdateProfileViewReactor) { KingfisherManager.shared.download(strUrl: reactor.profile.profileImg?.url) { [weak self] image in - self?.updateProfileView.image = image ?? .init(.image(.sooumLogo)) + self?.updateProfileView.image = image ?? .init(.image(.defaultStyle(.sooumLogo))) } self.updateProfileView.text = reactor.profile.nickname diff --git a/SOOUM/SOOUM/Presentations/Main/Profile/UpdateProfile/Views/UpdateProfileView.swift b/SOOUM/SOOUM/Presentations/Main/Profile/UpdateProfile/Views/UpdateProfileView.swift index 150ab34e..3dd6e800 100644 --- a/SOOUM/SOOUM/Presentations/Main/Profile/UpdateProfile/Views/UpdateProfileView.swift +++ b/SOOUM/SOOUM/Presentations/Main/Profile/UpdateProfile/Views/UpdateProfileView.swift @@ -19,7 +19,7 @@ class UpdateProfileView: UIView { } private let profileImageView = UIImageView().then { - $0.image = .init(.image(.sooumLogo)) + $0.image = .init(.image(.defaultStyle(.sooumLogo))) $0.layer.cornerRadius = 128 * 0.5 $0.clipsToBounds = true } @@ -81,7 +81,7 @@ class UpdateProfileView: UIView { } private let errorImageView = UIImageView().then { - $0.image = .init(.image(.errorTriangle)) + $0.image = .init(.image(.defaultStyle(.errorTriangle))) } private let errorMessageLabel = UILabel().then { diff --git a/SOOUM/SOOUM/Presentations/Main/WriteCard/UploadCard/UploadCardBottomSheetViewReactor.swift b/SOOUM/SOOUM/Presentations/Main/WriteCard/UploadCard/UploadCardBottomSheetViewReactor.swift index 83be395c..30f24ef7 100644 --- a/SOOUM/SOOUM/Presentations/Main/WriteCard/UploadCard/UploadCardBottomSheetViewReactor.swift +++ b/SOOUM/SOOUM/Presentations/Main/WriteCard/UploadCard/UploadCardBottomSheetViewReactor.swift @@ -64,9 +64,11 @@ class UploadCardBottomSheetViewReactor: Reactor { func mutate(action: Action) -> Observable { switch action { case .fetchNewDefaultImage: - return fetchDefaultImages() + // return fetchDefaultImages() + return .empty() case let .seleteMyImage(myImage): - return uploadMyImage(myImage: myImage) + // return uploadMyImage(myImage: myImage) + return .empty() } } @@ -82,29 +84,29 @@ class UploadCardBottomSheetViewReactor: Reactor { return state } - func fetchDefaultImages() -> Observable { - - let request: UploadRequest = .defaultImages - - return self.provider.networkManager.request(DefaultCardImageResponse.self, request: request) - .map(\.embedded.imgURLInfoList) - .flatMap { imageInfoList -> Observable in - let imageURLWithNames: [ImageURLWithName] = imageInfoList.map { - ImageURLWithName(name: $0.imgName, urlString: $0.url.href) - } - return Observable.from(imageURLWithNames) - .withUnretained(self) - .flatMap { object, imageURLWithName -> Observable in - object.downloadImage(imageURLWithName: imageURLWithName) - } // 각 ImageURLWithName를 옵저버블로 바꾼 후 - .compactMap { $0 } // nil 값 제거 - .toArray() - .asObservable() - .map { imagesWithNames in - return Mutation.defaultImages(imagesWithNames) - } - } - } +// func fetchDefaultImages() -> Observable { +// +// let request: UploadRequest = .defaultImages +// +// return self.provider.networkManager.request(DefaultCardImageResponse.self, request: request) +// .map(\.embedded.imgURLInfoList) +// .flatMap { imageInfoList -> Observable in +// let imageURLWithNames: [ImageURLWithName] = imageInfoList.map { +// ImageURLWithName(name: $0.imgName, urlString: $0.url.href) +// } +// return Observable.from(imageURLWithNames) +// .withUnretained(self) +// .flatMap { object, imageURLWithName -> Observable in +// object.downloadImage(imageURLWithName: imageURLWithName) +// } // 각 ImageURLWithName를 옵저버블로 바꾼 후 +// .compactMap { $0 } // nil 값 제거 +// .toArray() +// .asObservable() +// .map { imagesWithNames in +// return Mutation.defaultImages(imagesWithNames) +// } +// } +// } private func downloadImage(imageURLWithName: ImageURLWithName) -> Observable { guard let url = URL(string: imageURLWithName.urlString) else { @@ -131,35 +133,35 @@ class UploadCardBottomSheetViewReactor: Reactor { } } - func uploadMyImage(myImage: UIImage) -> Observable { - - let presignedURLRequest: UploadRequest = .presignedURL - - return self.provider.networkManager.request(PresignedStorageResponse.self, request: presignedURLRequest) - .flatMap { [weak self] presignedResponse -> Observable in - guard let self = self else { return Observable.just(Mutation.myImageName("")) } - - // 2. presigned URL을 통해 이미지를 업로드합니다. - guard let url = URL(string: presignedResponse.url.url) else { - return Observable.just(Mutation.myImageName("")) - } - - return Observable.create { observer in - self.uploadImageToURL(image: myImage, url: url) { result in - switch result { - case .success: - observer.onNext(Mutation.myImageName(presignedResponse.imgName)) - observer.onCompleted() - case .failure(let error): - // 4. 실패 시 에러 방출 - observer.onError(error) - } - } - - return Disposables.create() - } - } - } +// func uploadMyImage(myImage: UIImage) -> Observable { +// +// let presignedURLRequest: UploadRequest = .presignedURL +// +// return self.provider.networkManager.request(PresignedStorageResponse.self, request: presignedURLRequest) +// .flatMap { [weak self] presignedResponse -> Observable in +// guard let self = self else { return Observable.just(Mutation.myImageName("")) } +// +// // 2. presigned URL을 통해 이미지를 업로드합니다. +// guard let url = URL(string: presignedResponse.url.url) else { +// return Observable.just(Mutation.myImageName("")) +// } +// +// return Observable.create { observer in +// self.uploadImageToURL(image: myImage, url: url) { result in +// switch result { +// case .success: +// observer.onNext(Mutation.myImageName(presignedResponse.imgName)) +// observer.onCompleted() +// case .failure(let error): +// // 4. 실패 시 에러 방출 +// observer.onError(error) +// } +// } +// +// return Disposables.create() +// } +// } +// } func uploadImageToURL(image: UIImage, url: URL, completion: @escaping (Result) -> Void) { diff --git a/SOOUM/SOOUM/Resources/Alamofire/Request/CardRequest.swift b/SOOUM/SOOUM/Resources/Alamofire/Request/CardRequest.swift index 3b7f5a60..fb3167a9 100644 --- a/SOOUM/SOOUM/Resources/Alamofire/Request/CardRequest.swift +++ b/SOOUM/SOOUM/Resources/Alamofire/Request/CardRequest.swift @@ -234,14 +234,10 @@ enum CardRequest: BaseRequest { var authorizationType: AuthorizationType { return .access } - - var version: APIVersion { - return .v1 - } func asURLRequest() throws -> URLRequest { - let pathWithAPIVersion = self.path + self.version.rawValue + let pathWithAPIVersion = self.path if let url = URL(string: Constants.endpoint)?.appendingPathComponent(pathWithAPIVersion) { var request = URLRequest(url: url) request.method = self.method diff --git a/SOOUM/SOOUM/Resources/Alamofire/Request/ConfigureRequest.swift b/SOOUM/SOOUM/Resources/Alamofire/Request/ConfigureRequest.swift index 07420437..7b0e353b 100644 --- a/SOOUM/SOOUM/Resources/Alamofire/Request/ConfigureRequest.swift +++ b/SOOUM/SOOUM/Resources/Alamofire/Request/ConfigureRequest.swift @@ -45,15 +45,10 @@ enum ConfigureRequest: BaseRequest { var authorizationType: AuthorizationType { return .access } - - var version: APIVersion { - return .v1 - } func asURLRequest() throws -> URLRequest { - let pathWithAPIVersion = self.path + self.version.rawValue - if let url = URL(string: Constants.endpoint)?.appendingPathComponent(pathWithAPIVersion) { + if let url = URL(string: Constants.endpoint)?.appendingPathComponent(self.path) { var request = URLRequest(url: url) request.method = self.method diff --git a/SOOUM/SOOUM/Resources/Alamofire/Request/JoinRequest.swift b/SOOUM/SOOUM/Resources/Alamofire/Request/JoinRequest.swift index 804f5d1b..06243fe5 100644 --- a/SOOUM/SOOUM/Resources/Alamofire/Request/JoinRequest.swift +++ b/SOOUM/SOOUM/Resources/Alamofire/Request/JoinRequest.swift @@ -74,15 +74,10 @@ enum JoinRequest: BaseRequest { return .access } } - - var version: APIVersion { - return .v1 - } func asURLRequest() throws -> URLRequest { - let pathWithAPIVersion = self.path + self.version.rawValue - if let url = URL(string: Constants.endpoint)?.appendingPathComponent(pathWithAPIVersion) { + if let url = URL(string: Constants.endpoint)?.appendingPathComponent(self.path) { var request = URLRequest(url: url) request.method = self.method diff --git a/SOOUM/SOOUM/Resources/Alamofire/Request/NotificationRequest.swift b/SOOUM/SOOUM/Resources/Alamofire/Request/NotificationRequest.swift deleted file mode 100644 index 3a626d08..00000000 --- a/SOOUM/SOOUM/Resources/Alamofire/Request/NotificationRequest.swift +++ /dev/null @@ -1,132 +0,0 @@ -// -// NotificationRequest.swift -// SOOUM -// -// Created by 오현식 on 12/23/24. -// - -import Foundation - -import Alamofire - - -enum NotificationRequest: BaseRequest { - - case totalWithoutRead(lastId: String?) - case totalRead(lastId: String?) - case totalWithoutReadCount - case commentWithoutRead(lastId: String?) - case commentRead(lastId: String?) - case commentWithoutReadCount - case likeWithoutRead(lastId: String?) - case likeRead(lastId: String?) - case likeWihoutReadCount - case requestRead(notificationId: String) - - var path: String { - switch self { - case let .totalWithoutRead(lastId): - if let lastId = lastId { - return "/notifications/unread/\(lastId)" - } else { - return "/notifications/unread" - } - - case let .totalRead(lastId): - if let lastId = lastId { - return "/notifications/read/\(lastId)" - } else { - return "/notifications/read" - } - - case .totalWithoutReadCount: - return "/notifications/unread-cnt" - - case let .commentWithoutRead(lastId): - if let lastId = lastId { - return "/notifications/card/unread/\(lastId)" - } else { - return "/notifications/card/unread" - } - - case let .commentRead(lastId): - if let lastId = lastId { - return "/notifications/card/read/\(lastId)" - } else { - return "/notifications/card/read" - } - - case .commentWithoutReadCount: - return "/notifications/card/unread-cnt" - - case let .likeWithoutRead(lastId): - if let lastId = lastId { - return "/notifications/like/unread/\(lastId)" - } else { - return "/notifications/like/unread" - } - - case let .likeRead(lastId): - if let lastId = lastId { - return "/notifications/like/read/\(lastId)" - } else { - return "/notifications/like/read" - } - - case .likeWihoutReadCount: - return "/notifications/like/unread-cnt" - - case let .requestRead(notificationId): - return "/notifications/\(notificationId)/read" - } - } - - var method: HTTPMethod { - switch self { - case .requestRead: - return .patch - default: - return .get - } - } - - var parameters: Parameters { - return [:] - } - - var encoding: ParameterEncoding { - return URLEncoding.default - } - - var authorizationType: AuthorizationType { - return .access - } - - var version: APIVersion { - return .v1 - } - - func asURLRequest() throws -> URLRequest { - - let pathWithAPIVersion = self.path + self.version.rawValue - if let url = URL(string: Constants.endpoint)?.appendingPathComponent(pathWithAPIVersion) { - var request = URLRequest(url: url) - request.method = self.method - - request.setValue(self.authorizationType.rawValue, forHTTPHeaderField: "AuthorizationType") - request.setValue( - Constants.ContentType.json.rawValue, - forHTTPHeaderField: Constants.HTTPHeader.contentType.rawValue - ) - request.setValue( - Constants.ContentType.json.rawValue, - forHTTPHeaderField: Constants.HTTPHeader.acceptType.rawValue - ) - - let encoded = try encoding.encode(request, with: self.parameters) - return encoded - } else { - return URLRequest(url: URL(string: "")!) - } - } -} diff --git a/SOOUM/SOOUM/Resources/Alamofire/Request/ProfileRequest.swift b/SOOUM/SOOUM/Resources/Alamofire/Request/ProfileRequest.swift index 2b99f616..2694df99 100644 --- a/SOOUM/SOOUM/Resources/Alamofire/Request/ProfileRequest.swift +++ b/SOOUM/SOOUM/Resources/Alamofire/Request/ProfileRequest.swift @@ -128,15 +128,10 @@ enum ProfileRequest: BaseRequest { var authorizationType: AuthorizationType { return .access } - - var version: APIVersion { - return .v1 - } func asURLRequest() throws -> URLRequest { - let pathWithAPIVersion = self.path + self.version.rawValue - if let url = URL(string: Constants.endpoint)?.appendingPathComponent(pathWithAPIVersion) { + if let url = URL(string: Constants.endpoint)?.appendingPathComponent(self.path) { var request = URLRequest(url: url) request.method = self.method diff --git a/SOOUM/SOOUM/Resources/Alamofire/Request/ReportRequest.swift b/SOOUM/SOOUM/Resources/Alamofire/Request/ReportRequest.swift index 903ae912..2cb3d121 100644 --- a/SOOUM/SOOUM/Resources/Alamofire/Request/ReportRequest.swift +++ b/SOOUM/SOOUM/Resources/Alamofire/Request/ReportRequest.swift @@ -60,15 +60,10 @@ enum ReportRequest: BaseRequest { var authorizationType: AuthorizationType { return .access } - - var version: APIVersion { - return .v1 - } func asURLRequest() throws -> URLRequest { - let pathWithAPIVersion = self.path + self.version.rawValue - if let url = URL(string: Constants.endpoint)?.appendingPathComponent(pathWithAPIVersion) { + if let url = URL(string: Constants.endpoint)?.appendingPathComponent(self.path) { var request = URLRequest(url: url) request.method = self.method diff --git a/SOOUM/SOOUM/Resources/Alamofire/Request/SettingsRequest.swift b/SOOUM/SOOUM/Resources/Alamofire/Request/SettingsRequest.swift index 4b5ba23c..2ad392c9 100644 --- a/SOOUM/SOOUM/Resources/Alamofire/Request/SettingsRequest.swift +++ b/SOOUM/SOOUM/Resources/Alamofire/Request/SettingsRequest.swift @@ -94,15 +94,10 @@ enum SettingsRequest: BaseRequest { var authorizationType: AuthorizationType { return .access } - - var version: APIVersion { - return .v1 - } func asURLRequest() throws -> URLRequest { - let pathWithAPIVersion = self.path + self.version.rawValue - if let url = URL(string: Constants.endpoint)?.appendingPathComponent(pathWithAPIVersion) { + if let url = URL(string: Constants.endpoint)?.appendingPathComponent(self.path) { var request = URLRequest(url: url) request.method = self.method diff --git a/SOOUM/SOOUM/Resources/Alamofire/Request/TagRequest.swift b/SOOUM/SOOUM/Resources/Alamofire/Request/TagRequest.swift index 0420e1f6..b5566112 100644 --- a/SOOUM/SOOUM/Resources/Alamofire/Request/TagRequest.swift +++ b/SOOUM/SOOUM/Resources/Alamofire/Request/TagRequest.swift @@ -85,15 +85,10 @@ enum TagRequest: BaseRequest { var authorizationType: AuthorizationType { return .access } - - var version: APIVersion { - return .v1 - } func asURLRequest() throws -> URLRequest { - let pathWithAPIVersion = self.path + self.version.rawValue - if let url = URL(string: Constants.endpoint)?.appendingPathComponent(pathWithAPIVersion) { + if let url = URL(string: Constants.endpoint)?.appendingPathComponent(self.path) { var request = URLRequest(url: url) request.method = self.method diff --git a/SOOUM/SOOUM/Resources/Alamofire/Request/UploadRequest.swift b/SOOUM/SOOUM/Resources/Alamofire/Request/UploadRequest.swift deleted file mode 100644 index 1d630034..00000000 --- a/SOOUM/SOOUM/Resources/Alamofire/Request/UploadRequest.swift +++ /dev/null @@ -1,85 +0,0 @@ -// -// UploadRequest.swift -// SOOUM -// -// Created by JDeoks on 10/23/24. -// - -import UIKit - -import Alamofire - -enum UploadRequest: BaseRequest { - - case defaultImages - /// 이미지 업로드할 url, 이미지 이름 fetch - case presignedURL - /// 이미지를 URL로 업로드 - case uploadMyImage(image: UIImage, presignedURL: URL) - - var path: String { - switch self { - case .defaultImages: - return "/imgs/default" - case .presignedURL: - return "/imgs/cards/upload" - case .uploadMyImage: - return "" // presignedURL로 직접 요청 - } - } - - var method: HTTPMethod { - switch self { - case .uploadMyImage: - return .put - case .defaultImages, .presignedURL: - return .get - } - } - - var parameters: Parameters { - switch self { - case .defaultImages, .uploadMyImage: - return [:] - case .presignedURL: - return ["extension": "jpeg"] - } - } - - var encoding: ParameterEncoding { - return URLEncoding.queryString - } - - var authorizationType: AuthorizationType { - return .access - } - - var version: APIVersion { - return .v1 - } - - func asURLRequest() throws -> URLRequest { - - let pathWithAPIVersion = self.path + self.version.rawValue - if let url = URL(string: Constants.endpoint)?.appendingPathComponent(pathWithAPIVersion) { - var request = URLRequest(url: url) - request.method = self.method - - request.setValue(self.authorizationType.rawValue, forHTTPHeaderField: "AuthorizationType") - request.setValue( - Constants.ContentType.json.rawValue, - forHTTPHeaderField: Constants.HTTPHeader.contentType.rawValue - ) - request.setValue( - Constants.ContentType.json.rawValue, - forHTTPHeaderField: Constants.HTTPHeader.acceptType.rawValue - ) - - let encoded = try encoding.encode(request, with: self.parameters) - return encoded - } else { - return URLRequest(url: URL(string: "")!) - } - } - -} diff --git a/SOOUM/SOOUM/Resources/Alamofire/Request/AuthRequest.swift b/SOOUM/SOOUM/Resources/Alamofire/Request/V2/AuthRequest.swift similarity index 51% rename from SOOUM/SOOUM/Resources/Alamofire/Request/AuthRequest.swift rename to SOOUM/SOOUM/Resources/Alamofire/Request/V2/AuthRequest.swift index 72a417c7..324f33e4 100644 --- a/SOOUM/SOOUM/Resources/Alamofire/Request/AuthRequest.swift +++ b/SOOUM/SOOUM/Resources/Alamofire/Request/V2/AuthRequest.swift @@ -5,87 +5,101 @@ // Created by 오현식 on 10/26/24. // -import Foundation - import Alamofire enum AuthRequest: BaseRequest { /// RSA 공개 키 요청 - case getPublicKey - /// 로그인 - case login(encryptedDeviceId: String) + case publicKey /// 회원가입 case signUp( encryptedDeviceId: String, - isAllowNotify: Bool, - isAllowTermOne: Bool, - isAllowTermTwo: Bool, - isAllowTermThree: Bool + isNotificationAgreed: Bool, + nickname: String, + profileImageName: String? ) + /// 로그인 + case login(encryptedDeviceId: String) /// 재인증 - case reAuthenticationWithRefreshSession - /// fcm 업데이트 - case updateFCM(fcmToken: String) - /// version 검사 - case updateCheck + case reAuthenticationWithRefreshSession(token: Token) + #if DEVELOP + /// 테스트 용 계정 삭제 + case withdraw(token: Token) + #endif var path: String { switch self { - case .getPublicKey: - return "/users/key" - case .login: - return "/users/login" + case .publicKey: + return "/api/rsa/public-key" case .signUp: - return "/users/sign-up" + return "/api/auth/sign-up" + case .login: + return "/api/auth/login" case .reAuthenticationWithRefreshSession: - return "/users/token" - case .updateFCM: - return "/members/fcm" - case .updateCheck: - return "/app/version/ios" + return "/api/auth/token/reissue" + #if DEVELOP + case .withdraw: + return "/api/auth/withdrawal" + #endif } } var method: HTTPMethod { switch self { - case .getPublicKey, .updateCheck: + case .publicKey: return .get case .login, .signUp, .reAuthenticationWithRefreshSession: return .post - case .updateFCM: - return .patch + #if DEVELOP + case .withdraw: + return .delete + #endif } } var parameters: Parameters { switch self { - case let .login(encryptedDeviceId): - return ["encryptedDeviceId": encryptedDeviceId] - case let .signUp( - encryptedDeviceId, - isAllowNotify, - isAllowTermOne, - isAllowTermTwo, - isAllowTermThree - ): - return [ - "memberInfo": [ + case let .signUp(encryptedDeviceId, isNotificationAgreed, nickname, profileImageName): + var memberInfo: [String: Any] + if let profileImageName = profileImageName { + memberInfo = [ "encryptedDeviceId": encryptedDeviceId, "deviceType": "IOS", - "isAllowNotify": isAllowNotify - ] as [String: Any], + "isNotificationAgreed": isNotificationAgreed, + "nickname": nickname, + "profileImage": profileImageName + ] + } else { + memberInfo = [ + "encryptedDeviceId": encryptedDeviceId, + "deviceType": "IOS", + "isNotificationAgreed": isNotificationAgreed, + "nickname": nickname + ] + } + return [ + "memberInfo": memberInfo, "policy": [ - "isAllowTermOne": isAllowTermOne, - "isAllowTermTwo": isAllowTermTwo, - "isAllowTermThree": isAllowTermThree + "agreedToTermsOfService": true, + "agreedToLocationTerms": true, + "agreedToPrivacyPolicy": true ] as [String: Any] ] - case let .updateFCM(fcmToken): - return ["fcmToken": fcmToken] - case .updateCheck: - return ["version": Version.thisAppVersion] + case let .login(encryptedDeviceId): + return ["encryptedDeviceId": encryptedDeviceId] + case let .reAuthenticationWithRefreshSession(token): + return [ + "accessToken": token.accessToken, + "refreshToken": token.refreshToken + ] + #if DEVELOP + case let .withdraw(token): + return [ + "accessToken": token.accessToken, + "refreshToken": token.refreshToken + ] + #endif default: return [:] } @@ -93,37 +107,27 @@ enum AuthRequest: BaseRequest { var encoding: ParameterEncoding { switch self { - case .login, .signUp, .updateFCM: - return JSONEncoding.default - default: + case .publicKey: return URLEncoding.default + default: + return JSONEncoding.default } } var authorizationType: AuthorizationType { switch self { - case .updateFCM: + #if DEVELOP + case .withdraw: return .access - case .reAuthenticationWithRefreshSession: - return .refresh + #endif default: return .none } } - var version: APIVersion { - switch self { - case .updateCheck: - return .v2 - default: - return .v1 - } - } - func asURLRequest() throws -> URLRequest { - let pathWithAPIVersion = self.path + self.version.rawValue - if let url = URL(string: Constants.endpoint)?.appendingPathComponent(pathWithAPIVersion) { + if let url = URL(string: Constants.endpoint)?.appendingPathComponent(self.path) { var request = URLRequest(url: url) request.method = self.method @@ -140,7 +144,7 @@ enum AuthRequest: BaseRequest { let encoded = try self.encoding.encode(request, with: self.parameters) return encoded } else { - return URLRequest(url: URL(string: "")!) + return .init(url: URL(string: "")!) } } } diff --git a/SOOUM/SOOUM/Resources/Alamofire/Request/V2/NotificationRequest.swift b/SOOUM/SOOUM/Resources/Alamofire/Request/V2/NotificationRequest.swift new file mode 100644 index 00000000..005d131c --- /dev/null +++ b/SOOUM/SOOUM/Resources/Alamofire/Request/V2/NotificationRequest.swift @@ -0,0 +1,138 @@ +// +// NotificationRequest.swift +// SOOUM +// +// Created by 오현식 on 12/23/24. +// + +import Alamofire + + +enum NotificationRequest: BaseRequest { + + /// 읽지 않은 알림 전체 조회 + case unreadNotifications(lastId: String?) + /// 읽지 않은 카드 알림 조회 + case unreadCardNotifications(lastId: String?) + /// 읽지 않은 팔로우 알림 조회 + case unreadFollowNotifications(lastId: String?) + /// 읽지 않은 공지 알림 조회 + case unreadNoticeNoticeNotifications(lastId: String?) + /// 읽은 알림 전체 조회 + case readNotifications(lastId: String?) + /// 읽은 카드 알림 조회 + case readCardNotifications(lastId: String?) + /// 읽은 팔로우 알림 조회 + case readFollowNotifications(lastId: String?) + /// 읽은 공지 알림 조회 + case readNoticeNoticeNotifications(lastId: String?) + /// 알림 읽음 요청 + case requestRead(notificationId: String) + + var path: String { + switch self { + case let .unreadNotifications(lastId): + if let lastId = lastId { + return "/api/notifications/unread/\(lastId)" + } else { + return "/api/notifications/unread" + } + + case let .unreadCardNotifications(lastId): + if let lastId = lastId { + return "/api/notifications/unread/card/\(lastId)" + } else { + return "/api/notifications/unread/card" + } + + case let .unreadFollowNotifications(lastId): + if let lastId = lastId { + return "/api/notifications/unread/follow/\(lastId)" + } else { + return "/api/notifications/unread/follow" + } + + case let .unreadNoticeNoticeNotifications(lastId): + if let lastId = lastId { + return "/api/notifications/unread/notice/\(lastId)" + } else { + return "/api/notifications/unread/notice" + } + + case let .readNotifications(lastId): + if let lastId = lastId { + return "/api/notifications/read/\(lastId)" + } else { + return "/api/notifications/read" + } + + case let .readCardNotifications(lastId): + if let lastId = lastId { + return "/api/notifications/read/card/\(lastId)" + } else { + return "/api/notifications/read/card" + } + + case let .readFollowNotifications(lastId): + if let lastId = lastId { + return "/api/notifications/read/follow/\(lastId)" + } else { + return "/api/notifications/read/follow" + } + + case let .readNoticeNoticeNotifications(lastId): + if let lastId = lastId { + return "/api/notifications/read/notice/\(lastId)" + } else { + return "/api/notifications/read/notice" + } + + case let .requestRead(notificationId): + return "/api/notifications/\(notificationId)/read" + } + } + + var method: HTTPMethod { + switch self { + case .requestRead: + return .patch + default: + return .get + } + } + + var parameters: Parameters { + return [:] + } + + var encoding: ParameterEncoding { + return URLEncoding.default + } + + var authorizationType: AuthorizationType { + return .access + } + + func asURLRequest() throws -> URLRequest { + + if let url = URL(string: Constants.endpoint)?.appendingPathComponent(self.path) { + var request = URLRequest(url: url) + request.method = self.method + + request.setValue(self.authorizationType.rawValue, forHTTPHeaderField: "AuthorizationType") + request.setValue( + Constants.ContentType.json.rawValue, + forHTTPHeaderField: Constants.HTTPHeader.contentType.rawValue + ) + request.setValue( + Constants.ContentType.json.rawValue, + forHTTPHeaderField: Constants.HTTPHeader.acceptType.rawValue + ) + + let encoded = try encoding.encode(request, with: self.parameters) + return encoded + } else { + return .init(url: URL(string: "")!) + } + } +} diff --git a/SOOUM/SOOUM/Resources/Alamofire/Request/V2/UserRequest.swift b/SOOUM/SOOUM/Resources/Alamofire/Request/V2/UserRequest.swift new file mode 100644 index 00000000..0b6fccd2 --- /dev/null +++ b/SOOUM/SOOUM/Resources/Alamofire/Request/V2/UserRequest.swift @@ -0,0 +1,114 @@ +// +// UserRequest.swift +// SOOUM +// +// Created by 오현식 on 9/16/25. +// + +import Alamofire + +enum UserRequest: BaseRequest { + + /// 가입 가능 여부 확인 + case checkAvailable(encryptedDeviceId: String) + /// 추천 닉네임 조회 + case nickname + /// 닉네임 유효성 검사 + case validateNickname(nickname: String) + /// 닉네임 업데이트 + case updateNickname(nickname: String) + /// 이미지 업로드할 공간 확보 + case presignedURL + /// 이미지 업데이트 + case updateImage(imageName: String) + /// fcmToken 업데이트 + case updateFCMToken(fcmToken: String) + + var path: String { + switch self { + case .checkAvailable: + return "/api/members/check-available" + case .nickname: + return "/api/members/generate-nickname" + case .validateNickname: + return "/api/members/validate-nickname" + case .updateNickname: + return "/api/members/nickname" + case .presignedURL: + return "/api/images/profile" + case .updateImage: + return "/api/members/profile-img" + case .updateFCMToken: + return "/api/members/fcm" + } + } + + var method: HTTPMethod { + switch self { + case .checkAvailable, .validateNickname, .presignedURL: + return .post + case .updateNickname, .updateImage, .updateFCMToken: + return .patch + case .nickname: + return .get + } + } + + var parameters: Parameters { + switch self { + case let .checkAvailable(encryptedDeviceId): + return ["encryptedDeviceId": encryptedDeviceId] + case let .updateNickname(nickname): + return ["nickname": nickname] + case let .validateNickname(nickname): + return ["nickname": nickname] + case let .updateImage(imageName): + return ["name": imageName] + case let .updateFCMToken(fcmToken): + return ["fcmToken": fcmToken] + default: + return [:] + } + } + + var encoding: ParameterEncoding { + switch self { + case .nickname: + return URLEncoding.default + default: + return JSONEncoding.default + } + } + + var authorizationType: AuthorizationType { + switch self{ + case .updateFCMToken: + return .access + default: + return .none + } + } + + func asURLRequest() throws -> URLRequest { + + if let url = URL(string: Constants.endpoint)?.appendingPathComponent(self.path) { + var request = URLRequest(url: url) + request.method = self.method + + request.setValue(self.authorizationType.rawValue, forHTTPHeaderField: "AuthorizationType") + request.setValue( + Constants.ContentType.json.rawValue, + forHTTPHeaderField: Constants.HTTPHeader.contentType.rawValue + ) + request.setValue( + Constants.ContentType.json.rawValue, + forHTTPHeaderField: Constants.HTTPHeader.acceptType.rawValue + ) + + let encoded = try self.encoding.encode(request, with: self.parameters) + return encoded + } else { + return .init(url: URL(string: "")!) + } + } +} diff --git a/SOOUM/SOOUM/Resources/Alamofire/Request/V2/VersionRequest.swift b/SOOUM/SOOUM/Resources/Alamofire/Request/V2/VersionRequest.swift new file mode 100644 index 00000000..5e8eae36 --- /dev/null +++ b/SOOUM/SOOUM/Resources/Alamofire/Request/V2/VersionRequest.swift @@ -0,0 +1,61 @@ +// +// VersionRequest.swift +// SOOUM +// +// Created by 오현식 on 9/16/25. +// + +import Alamofire + +enum VersionRequest: BaseRequest { + + case version + + var path: String { + #if PRODUCTION + /// 구버전 업데이트를 위한 API + return "app/version/ios/v2" + #elseif DEVELOP + return "/api/version/IOS" + #endif + } + + var method: HTTPMethod { + return .get + } + + var parameters: Parameters { + return ["version": Info.appVersion] + } + + var encoding: ParameterEncoding { + return URLEncoding.default + } + + var authorizationType: AuthorizationType { + return .none + } + + func asURLRequest() throws -> URLRequest { + + if let url = URL(string: Constants.endpoint)?.appendingPathComponent(self.path) { + var request = URLRequest(url: url) + request.method = self.method + + request.setValue(self.authorizationType.rawValue, forHTTPHeaderField: "AuthorizationType") + request.setValue( + Constants.ContentType.json.rawValue, + forHTTPHeaderField: Constants.HTTPHeader.contentType.rawValue + ) + request.setValue( + Constants.ContentType.json.rawValue, + forHTTPHeaderField: Constants.HTTPHeader.acceptType.rawValue + ) + + let encoded = try self.encoding.encode(request, with: self.parameters) + return encoded + } else { + return .init(url: URL(string: "")!) + } + } +} diff --git a/SOOUM/SOOUM/Resources/Assets.xcassets/AppIcon-Dev.appiconset/100.png b/SOOUM/SOOUM/Resources/Assets.xcassets/AppIcon-Dev.appiconset/100.png index 4a35df96..44d76cd6 100644 Binary files a/SOOUM/SOOUM/Resources/Assets.xcassets/AppIcon-Dev.appiconset/100.png and b/SOOUM/SOOUM/Resources/Assets.xcassets/AppIcon-Dev.appiconset/100.png differ diff --git a/SOOUM/SOOUM/Resources/Assets.xcassets/AppIcon-Dev.appiconset/1024.png b/SOOUM/SOOUM/Resources/Assets.xcassets/AppIcon-Dev.appiconset/1024.png index e2b1b5b9..8e5ab540 100644 Binary files a/SOOUM/SOOUM/Resources/Assets.xcassets/AppIcon-Dev.appiconset/1024.png and b/SOOUM/SOOUM/Resources/Assets.xcassets/AppIcon-Dev.appiconset/1024.png differ diff --git a/SOOUM/SOOUM/Resources/Assets.xcassets/AppIcon-Dev.appiconset/114.png b/SOOUM/SOOUM/Resources/Assets.xcassets/AppIcon-Dev.appiconset/114.png index 1b95e1a9..efa3aab9 100644 Binary files a/SOOUM/SOOUM/Resources/Assets.xcassets/AppIcon-Dev.appiconset/114.png and b/SOOUM/SOOUM/Resources/Assets.xcassets/AppIcon-Dev.appiconset/114.png differ diff --git a/SOOUM/SOOUM/Resources/Assets.xcassets/AppIcon-Dev.appiconset/120.png b/SOOUM/SOOUM/Resources/Assets.xcassets/AppIcon-Dev.appiconset/120.png index 9c700c6f..7292ffa4 100644 Binary files a/SOOUM/SOOUM/Resources/Assets.xcassets/AppIcon-Dev.appiconset/120.png and b/SOOUM/SOOUM/Resources/Assets.xcassets/AppIcon-Dev.appiconset/120.png differ diff --git a/SOOUM/SOOUM/Resources/Assets.xcassets/AppIcon-Dev.appiconset/144.png b/SOOUM/SOOUM/Resources/Assets.xcassets/AppIcon-Dev.appiconset/144.png index c5c24c75..0272ad78 100644 Binary files a/SOOUM/SOOUM/Resources/Assets.xcassets/AppIcon-Dev.appiconset/144.png and b/SOOUM/SOOUM/Resources/Assets.xcassets/AppIcon-Dev.appiconset/144.png differ diff --git a/SOOUM/SOOUM/Resources/Assets.xcassets/AppIcon-Dev.appiconset/152.png b/SOOUM/SOOUM/Resources/Assets.xcassets/AppIcon-Dev.appiconset/152.png index 4dfab833..9af43eb6 100644 Binary files a/SOOUM/SOOUM/Resources/Assets.xcassets/AppIcon-Dev.appiconset/152.png and b/SOOUM/SOOUM/Resources/Assets.xcassets/AppIcon-Dev.appiconset/152.png differ diff --git a/SOOUM/SOOUM/Resources/Assets.xcassets/AppIcon-Dev.appiconset/167.png b/SOOUM/SOOUM/Resources/Assets.xcassets/AppIcon-Dev.appiconset/167.png index be1df57c..3f0db64f 100644 Binary files a/SOOUM/SOOUM/Resources/Assets.xcassets/AppIcon-Dev.appiconset/167.png and b/SOOUM/SOOUM/Resources/Assets.xcassets/AppIcon-Dev.appiconset/167.png differ diff --git a/SOOUM/SOOUM/Resources/Assets.xcassets/AppIcon-Dev.appiconset/180.png b/SOOUM/SOOUM/Resources/Assets.xcassets/AppIcon-Dev.appiconset/180.png index 16b832d1..d0df52c7 100644 Binary files a/SOOUM/SOOUM/Resources/Assets.xcassets/AppIcon-Dev.appiconset/180.png and b/SOOUM/SOOUM/Resources/Assets.xcassets/AppIcon-Dev.appiconset/180.png differ diff --git a/SOOUM/SOOUM/Resources/Assets.xcassets/AppIcon-Dev.appiconset/20.png b/SOOUM/SOOUM/Resources/Assets.xcassets/AppIcon-Dev.appiconset/20.png index f8922382..74e1923e 100644 Binary files a/SOOUM/SOOUM/Resources/Assets.xcassets/AppIcon-Dev.appiconset/20.png and b/SOOUM/SOOUM/Resources/Assets.xcassets/AppIcon-Dev.appiconset/20.png differ diff --git a/SOOUM/SOOUM/Resources/Assets.xcassets/AppIcon-Dev.appiconset/29.png b/SOOUM/SOOUM/Resources/Assets.xcassets/AppIcon-Dev.appiconset/29.png index 4bf513c4..de76cd56 100644 Binary files a/SOOUM/SOOUM/Resources/Assets.xcassets/AppIcon-Dev.appiconset/29.png and b/SOOUM/SOOUM/Resources/Assets.xcassets/AppIcon-Dev.appiconset/29.png differ diff --git a/SOOUM/SOOUM/Resources/Assets.xcassets/AppIcon-Dev.appiconset/40.png b/SOOUM/SOOUM/Resources/Assets.xcassets/AppIcon-Dev.appiconset/40.png index 69566787..d074c7a5 100644 Binary files a/SOOUM/SOOUM/Resources/Assets.xcassets/AppIcon-Dev.appiconset/40.png and b/SOOUM/SOOUM/Resources/Assets.xcassets/AppIcon-Dev.appiconset/40.png differ diff --git a/SOOUM/SOOUM/Resources/Assets.xcassets/AppIcon-Dev.appiconset/50.png b/SOOUM/SOOUM/Resources/Assets.xcassets/AppIcon-Dev.appiconset/50.png index 7217a060..3ff7c180 100644 Binary files a/SOOUM/SOOUM/Resources/Assets.xcassets/AppIcon-Dev.appiconset/50.png and b/SOOUM/SOOUM/Resources/Assets.xcassets/AppIcon-Dev.appiconset/50.png differ diff --git a/SOOUM/SOOUM/Resources/Assets.xcassets/AppIcon-Dev.appiconset/57.png b/SOOUM/SOOUM/Resources/Assets.xcassets/AppIcon-Dev.appiconset/57.png index cfa791f7..d0c93c9c 100644 Binary files a/SOOUM/SOOUM/Resources/Assets.xcassets/AppIcon-Dev.appiconset/57.png and b/SOOUM/SOOUM/Resources/Assets.xcassets/AppIcon-Dev.appiconset/57.png differ diff --git a/SOOUM/SOOUM/Resources/Assets.xcassets/AppIcon-Dev.appiconset/58.png b/SOOUM/SOOUM/Resources/Assets.xcassets/AppIcon-Dev.appiconset/58.png index 1b55b774..68b30de5 100644 Binary files a/SOOUM/SOOUM/Resources/Assets.xcassets/AppIcon-Dev.appiconset/58.png and b/SOOUM/SOOUM/Resources/Assets.xcassets/AppIcon-Dev.appiconset/58.png differ diff --git a/SOOUM/SOOUM/Resources/Assets.xcassets/AppIcon-Dev.appiconset/60.png b/SOOUM/SOOUM/Resources/Assets.xcassets/AppIcon-Dev.appiconset/60.png index 9cf48da5..09c208d6 100644 Binary files a/SOOUM/SOOUM/Resources/Assets.xcassets/AppIcon-Dev.appiconset/60.png and b/SOOUM/SOOUM/Resources/Assets.xcassets/AppIcon-Dev.appiconset/60.png differ diff --git a/SOOUM/SOOUM/Resources/Assets.xcassets/AppIcon-Dev.appiconset/72.png b/SOOUM/SOOUM/Resources/Assets.xcassets/AppIcon-Dev.appiconset/72.png index 3c46f748..1a077882 100644 Binary files a/SOOUM/SOOUM/Resources/Assets.xcassets/AppIcon-Dev.appiconset/72.png and b/SOOUM/SOOUM/Resources/Assets.xcassets/AppIcon-Dev.appiconset/72.png differ diff --git a/SOOUM/SOOUM/Resources/Assets.xcassets/AppIcon-Dev.appiconset/76.png b/SOOUM/SOOUM/Resources/Assets.xcassets/AppIcon-Dev.appiconset/76.png index bda251ef..04731a3a 100644 Binary files a/SOOUM/SOOUM/Resources/Assets.xcassets/AppIcon-Dev.appiconset/76.png and b/SOOUM/SOOUM/Resources/Assets.xcassets/AppIcon-Dev.appiconset/76.png differ diff --git a/SOOUM/SOOUM/Resources/Assets.xcassets/AppIcon-Dev.appiconset/80.png b/SOOUM/SOOUM/Resources/Assets.xcassets/AppIcon-Dev.appiconset/80.png index 5602cf8c..2e8c162f 100644 Binary files a/SOOUM/SOOUM/Resources/Assets.xcassets/AppIcon-Dev.appiconset/80.png and b/SOOUM/SOOUM/Resources/Assets.xcassets/AppIcon-Dev.appiconset/80.png differ diff --git a/SOOUM/SOOUM/Resources/Assets.xcassets/AppIcon-Dev.appiconset/87.png b/SOOUM/SOOUM/Resources/Assets.xcassets/AppIcon-Dev.appiconset/87.png index 7239401d..946147a1 100644 Binary files a/SOOUM/SOOUM/Resources/Assets.xcassets/AppIcon-Dev.appiconset/87.png and b/SOOUM/SOOUM/Resources/Assets.xcassets/AppIcon-Dev.appiconset/87.png differ diff --git a/SOOUM/SOOUM/Resources/Assets.xcassets/AppIcon.appiconset/100.png b/SOOUM/SOOUM/Resources/Assets.xcassets/AppIcon.appiconset/100.png index 4a35df96..44d76cd6 100644 Binary files a/SOOUM/SOOUM/Resources/Assets.xcassets/AppIcon.appiconset/100.png and b/SOOUM/SOOUM/Resources/Assets.xcassets/AppIcon.appiconset/100.png differ diff --git a/SOOUM/SOOUM/Resources/Assets.xcassets/AppIcon.appiconset/1024.png b/SOOUM/SOOUM/Resources/Assets.xcassets/AppIcon.appiconset/1024.png index e2b1b5b9..8e5ab540 100644 Binary files a/SOOUM/SOOUM/Resources/Assets.xcassets/AppIcon.appiconset/1024.png and b/SOOUM/SOOUM/Resources/Assets.xcassets/AppIcon.appiconset/1024.png differ diff --git a/SOOUM/SOOUM/Resources/Assets.xcassets/AppIcon.appiconset/114.png b/SOOUM/SOOUM/Resources/Assets.xcassets/AppIcon.appiconset/114.png index 1b95e1a9..efa3aab9 100644 Binary files a/SOOUM/SOOUM/Resources/Assets.xcassets/AppIcon.appiconset/114.png and b/SOOUM/SOOUM/Resources/Assets.xcassets/AppIcon.appiconset/114.png differ diff --git a/SOOUM/SOOUM/Resources/Assets.xcassets/AppIcon.appiconset/120.png b/SOOUM/SOOUM/Resources/Assets.xcassets/AppIcon.appiconset/120.png index 9c700c6f..7292ffa4 100644 Binary files a/SOOUM/SOOUM/Resources/Assets.xcassets/AppIcon.appiconset/120.png and b/SOOUM/SOOUM/Resources/Assets.xcassets/AppIcon.appiconset/120.png differ diff --git a/SOOUM/SOOUM/Resources/Assets.xcassets/AppIcon.appiconset/144.png b/SOOUM/SOOUM/Resources/Assets.xcassets/AppIcon.appiconset/144.png index c5c24c75..0272ad78 100644 Binary files a/SOOUM/SOOUM/Resources/Assets.xcassets/AppIcon.appiconset/144.png and b/SOOUM/SOOUM/Resources/Assets.xcassets/AppIcon.appiconset/144.png differ diff --git a/SOOUM/SOOUM/Resources/Assets.xcassets/AppIcon.appiconset/152.png b/SOOUM/SOOUM/Resources/Assets.xcassets/AppIcon.appiconset/152.png index 4dfab833..9af43eb6 100644 Binary files a/SOOUM/SOOUM/Resources/Assets.xcassets/AppIcon.appiconset/152.png and b/SOOUM/SOOUM/Resources/Assets.xcassets/AppIcon.appiconset/152.png differ diff --git a/SOOUM/SOOUM/Resources/Assets.xcassets/AppIcon.appiconset/167.png b/SOOUM/SOOUM/Resources/Assets.xcassets/AppIcon.appiconset/167.png index be1df57c..3f0db64f 100644 Binary files a/SOOUM/SOOUM/Resources/Assets.xcassets/AppIcon.appiconset/167.png and b/SOOUM/SOOUM/Resources/Assets.xcassets/AppIcon.appiconset/167.png differ diff --git a/SOOUM/SOOUM/Resources/Assets.xcassets/AppIcon.appiconset/180.png b/SOOUM/SOOUM/Resources/Assets.xcassets/AppIcon.appiconset/180.png index 16b832d1..d0df52c7 100644 Binary files a/SOOUM/SOOUM/Resources/Assets.xcassets/AppIcon.appiconset/180.png and b/SOOUM/SOOUM/Resources/Assets.xcassets/AppIcon.appiconset/180.png differ diff --git a/SOOUM/SOOUM/Resources/Assets.xcassets/AppIcon.appiconset/20.png b/SOOUM/SOOUM/Resources/Assets.xcassets/AppIcon.appiconset/20.png index f8922382..74e1923e 100644 Binary files a/SOOUM/SOOUM/Resources/Assets.xcassets/AppIcon.appiconset/20.png and b/SOOUM/SOOUM/Resources/Assets.xcassets/AppIcon.appiconset/20.png differ diff --git a/SOOUM/SOOUM/Resources/Assets.xcassets/AppIcon.appiconset/29.png b/SOOUM/SOOUM/Resources/Assets.xcassets/AppIcon.appiconset/29.png index 4bf513c4..de76cd56 100644 Binary files a/SOOUM/SOOUM/Resources/Assets.xcassets/AppIcon.appiconset/29.png and b/SOOUM/SOOUM/Resources/Assets.xcassets/AppIcon.appiconset/29.png differ diff --git a/SOOUM/SOOUM/Resources/Assets.xcassets/AppIcon.appiconset/40.png b/SOOUM/SOOUM/Resources/Assets.xcassets/AppIcon.appiconset/40.png index 69566787..d074c7a5 100644 Binary files a/SOOUM/SOOUM/Resources/Assets.xcassets/AppIcon.appiconset/40.png and b/SOOUM/SOOUM/Resources/Assets.xcassets/AppIcon.appiconset/40.png differ diff --git a/SOOUM/SOOUM/Resources/Assets.xcassets/AppIcon.appiconset/50.png b/SOOUM/SOOUM/Resources/Assets.xcassets/AppIcon.appiconset/50.png index 7217a060..3ff7c180 100644 Binary files a/SOOUM/SOOUM/Resources/Assets.xcassets/AppIcon.appiconset/50.png and b/SOOUM/SOOUM/Resources/Assets.xcassets/AppIcon.appiconset/50.png differ diff --git a/SOOUM/SOOUM/Resources/Assets.xcassets/AppIcon.appiconset/57.png b/SOOUM/SOOUM/Resources/Assets.xcassets/AppIcon.appiconset/57.png index cfa791f7..d0c93c9c 100644 Binary files a/SOOUM/SOOUM/Resources/Assets.xcassets/AppIcon.appiconset/57.png and b/SOOUM/SOOUM/Resources/Assets.xcassets/AppIcon.appiconset/57.png differ diff --git a/SOOUM/SOOUM/Resources/Assets.xcassets/AppIcon.appiconset/58.png b/SOOUM/SOOUM/Resources/Assets.xcassets/AppIcon.appiconset/58.png index 1b55b774..68b30de5 100644 Binary files a/SOOUM/SOOUM/Resources/Assets.xcassets/AppIcon.appiconset/58.png and b/SOOUM/SOOUM/Resources/Assets.xcassets/AppIcon.appiconset/58.png differ diff --git a/SOOUM/SOOUM/Resources/Assets.xcassets/AppIcon.appiconset/60.png b/SOOUM/SOOUM/Resources/Assets.xcassets/AppIcon.appiconset/60.png index 9cf48da5..09c208d6 100644 Binary files a/SOOUM/SOOUM/Resources/Assets.xcassets/AppIcon.appiconset/60.png and b/SOOUM/SOOUM/Resources/Assets.xcassets/AppIcon.appiconset/60.png differ diff --git a/SOOUM/SOOUM/Resources/Assets.xcassets/AppIcon.appiconset/72.png b/SOOUM/SOOUM/Resources/Assets.xcassets/AppIcon.appiconset/72.png index 3c46f748..1a077882 100644 Binary files a/SOOUM/SOOUM/Resources/Assets.xcassets/AppIcon.appiconset/72.png and b/SOOUM/SOOUM/Resources/Assets.xcassets/AppIcon.appiconset/72.png differ diff --git a/SOOUM/SOOUM/Resources/Assets.xcassets/AppIcon.appiconset/76.png b/SOOUM/SOOUM/Resources/Assets.xcassets/AppIcon.appiconset/76.png index bda251ef..04731a3a 100644 Binary files a/SOOUM/SOOUM/Resources/Assets.xcassets/AppIcon.appiconset/76.png and b/SOOUM/SOOUM/Resources/Assets.xcassets/AppIcon.appiconset/76.png differ diff --git a/SOOUM/SOOUM/Resources/Assets.xcassets/AppIcon.appiconset/80.png b/SOOUM/SOOUM/Resources/Assets.xcassets/AppIcon.appiconset/80.png index 5602cf8c..2e8c162f 100644 Binary files a/SOOUM/SOOUM/Resources/Assets.xcassets/AppIcon.appiconset/80.png and b/SOOUM/SOOUM/Resources/Assets.xcassets/AppIcon.appiconset/80.png differ diff --git a/SOOUM/SOOUM/Resources/Assets.xcassets/AppIcon.appiconset/87.png b/SOOUM/SOOUM/Resources/Assets.xcassets/AppIcon.appiconset/87.png index 7239401d..946147a1 100644 Binary files a/SOOUM/SOOUM/Resources/Assets.xcassets/AppIcon.appiconset/87.png and b/SOOUM/SOOUM/Resources/Assets.xcassets/AppIcon.appiconset/87.png differ diff --git a/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Contents.json b/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Contents.json new file mode 100644 index 00000000..73c00596 --- /dev/null +++ b/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Contents.json b/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Contents.json new file mode 100644 index 00000000..73c00596 --- /dev/null +++ b/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Filled/Contents.json b/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Filled/Contents.json new file mode 100644 index 00000000..73c00596 --- /dev/null +++ b/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Filled/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Filled/v2_bell_filled.imageset/Contents.json b/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Filled/v2_bell_filled.imageset/Contents.json new file mode 100644 index 00000000..4c288709 --- /dev/null +++ b/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Filled/v2_bell_filled.imageset/Contents.json @@ -0,0 +1,16 @@ +{ + "images" : [ + { + "filename" : "bell_filled.svg", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "preserves-vector-representation" : true, + "template-rendering-intent" : "template" + } +} diff --git a/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Filled/v2_bell_filled.imageset/bell_filled.svg b/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Filled/v2_bell_filled.imageset/bell_filled.svg new file mode 100644 index 00000000..09495b53 --- /dev/null +++ b/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Filled/v2_bell_filled.imageset/bell_filled.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Filled/v2_camera_filled.imageset/Contents.json b/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Filled/v2_camera_filled.imageset/Contents.json new file mode 100644 index 00000000..9f1e6b16 --- /dev/null +++ b/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Filled/v2_camera_filled.imageset/Contents.json @@ -0,0 +1,16 @@ +{ + "images" : [ + { + "filename" : "camera_filled.svg", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "preserves-vector-representation" : true, + "template-rendering-intent" : "template" + } +} diff --git a/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Filled/v2_camera_filled.imageset/camera_filled.svg b/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Filled/v2_camera_filled.imageset/camera_filled.svg new file mode 100644 index 00000000..f3e7e38f --- /dev/null +++ b/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Filled/v2_camera_filled.imageset/camera_filled.svg @@ -0,0 +1,3 @@ + + + diff --git a/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Filled/v2_danger_filled.imageset/Contents.json b/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Filled/v2_danger_filled.imageset/Contents.json new file mode 100644 index 00000000..80030d64 --- /dev/null +++ b/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Filled/v2_danger_filled.imageset/Contents.json @@ -0,0 +1,16 @@ +{ + "images" : [ + { + "filename" : "danger_filled.svg", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "preserves-vector-representation" : true, + "template-rendering-intent" : "template" + } +} diff --git a/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Filled/v2_danger_filled.imageset/danger_filled.svg b/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Filled/v2_danger_filled.imageset/danger_filled.svg new file mode 100644 index 00000000..ab0afdff --- /dev/null +++ b/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Filled/v2_danger_filled.imageset/danger_filled.svg @@ -0,0 +1,3 @@ + + + diff --git a/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Filled/v2_heart_filled.imageset/Contents.json b/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Filled/v2_heart_filled.imageset/Contents.json new file mode 100644 index 00000000..0788c549 --- /dev/null +++ b/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Filled/v2_heart_filled.imageset/Contents.json @@ -0,0 +1,16 @@ +{ + "images" : [ + { + "filename" : "heart_filled.svg", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "preserves-vector-representation" : true, + "template-rendering-intent" : "template" + } +} diff --git a/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Filled/v2_heart_filled.imageset/heart_filled.svg b/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Filled/v2_heart_filled.imageset/heart_filled.svg new file mode 100644 index 00000000..70efc3b8 --- /dev/null +++ b/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Filled/v2_heart_filled.imageset/heart_filled.svg @@ -0,0 +1,3 @@ + + + diff --git a/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Filled/v2_home_filled.imageset/Contents.json b/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Filled/v2_home_filled.imageset/Contents.json new file mode 100644 index 00000000..2da6cbcb --- /dev/null +++ b/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Filled/v2_home_filled.imageset/Contents.json @@ -0,0 +1,16 @@ +{ + "images" : [ + { + "filename" : "home_filled.svg", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "preserves-vector-representation" : true, + "template-rendering-intent" : "template" + } +} diff --git a/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Filled/v2_home_filled.imageset/home_filled.svg b/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Filled/v2_home_filled.imageset/home_filled.svg new file mode 100644 index 00000000..a9a8d688 --- /dev/null +++ b/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Filled/v2_home_filled.imageset/home_filled.svg @@ -0,0 +1,3 @@ + + + diff --git a/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Filled/v2_image_filled.imageset/Contents.json b/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Filled/v2_image_filled.imageset/Contents.json new file mode 100644 index 00000000..d5813415 --- /dev/null +++ b/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Filled/v2_image_filled.imageset/Contents.json @@ -0,0 +1,16 @@ +{ + "images" : [ + { + "filename" : "image_filled.svg", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "preserves-vector-representation" : true, + "template-rendering-intent" : "template" + } +} diff --git a/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Filled/v2_image_filled.imageset/image_filled.svg b/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Filled/v2_image_filled.imageset/image_filled.svg new file mode 100644 index 00000000..666130cf --- /dev/null +++ b/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Filled/v2_image_filled.imageset/image_filled.svg @@ -0,0 +1,4 @@ + + + + diff --git a/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Filled/v2_info_filled.imageset/Contents.json b/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Filled/v2_info_filled.imageset/Contents.json new file mode 100644 index 00000000..29b2fb8e --- /dev/null +++ b/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Filled/v2_info_filled.imageset/Contents.json @@ -0,0 +1,16 @@ +{ + "images" : [ + { + "filename" : "Info_filled.svg", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "preserves-vector-representation" : true, + "template-rendering-intent" : "template" + } +} diff --git a/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Filled/v2_info_filled.imageset/Info_filled.svg b/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Filled/v2_info_filled.imageset/Info_filled.svg new file mode 100644 index 00000000..2b426491 --- /dev/null +++ b/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Filled/v2_info_filled.imageset/Info_filled.svg @@ -0,0 +1,3 @@ + + + diff --git a/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Filled/v2_location_filled.imageset/Contents.json b/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Filled/v2_location_filled.imageset/Contents.json new file mode 100644 index 00000000..3fc0069d --- /dev/null +++ b/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Filled/v2_location_filled.imageset/Contents.json @@ -0,0 +1,16 @@ +{ + "images" : [ + { + "filename" : "location_filled.svg", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "preserves-vector-representation" : true, + "template-rendering-intent" : "template" + } +} diff --git a/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Filled/v2_location_filled.imageset/location_filled.svg b/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Filled/v2_location_filled.imageset/location_filled.svg new file mode 100644 index 00000000..e32e9ba2 --- /dev/null +++ b/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Filled/v2_location_filled.imageset/location_filled.svg @@ -0,0 +1,3 @@ + + + diff --git a/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Filled/v2_message_circle_filled.imageset/Contents.json b/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Filled/v2_message_circle_filled.imageset/Contents.json new file mode 100644 index 00000000..93b3da70 --- /dev/null +++ b/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Filled/v2_message_circle_filled.imageset/Contents.json @@ -0,0 +1,16 @@ +{ + "images" : [ + { + "filename" : "message_circle_filled.svg", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "preserves-vector-representation" : true, + "template-rendering-intent" : "template" + } +} diff --git a/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Filled/v2_message_circle_filled.imageset/message_circle_filled.svg b/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Filled/v2_message_circle_filled.imageset/message_circle_filled.svg new file mode 100644 index 00000000..8d30ff97 --- /dev/null +++ b/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Filled/v2_message_circle_filled.imageset/message_circle_filled.svg @@ -0,0 +1,3 @@ + + + diff --git a/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Filled/v2_message_square_filled.imageset/Contents.json b/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Filled/v2_message_square_filled.imageset/Contents.json new file mode 100644 index 00000000..6d1069e1 --- /dev/null +++ b/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Filled/v2_message_square_filled.imageset/Contents.json @@ -0,0 +1,16 @@ +{ + "images" : [ + { + "filename" : "message_square_filled.svg", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "preserves-vector-representation" : true, + "template-rendering-intent" : "template" + } +} diff --git a/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Filled/v2_message_square_filled.imageset/message_square_filled.svg b/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Filled/v2_message_square_filled.imageset/message_square_filled.svg new file mode 100644 index 00000000..f0b849d4 --- /dev/null +++ b/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Filled/v2_message_square_filled.imageset/message_square_filled.svg @@ -0,0 +1,3 @@ + + + diff --git a/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Filled/v2_settings_filled.imageset/Contents.json b/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Filled/v2_settings_filled.imageset/Contents.json new file mode 100644 index 00000000..671f5aed --- /dev/null +++ b/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Filled/v2_settings_filled.imageset/Contents.json @@ -0,0 +1,16 @@ +{ + "images" : [ + { + "filename" : "settings_filled.svg", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "preserves-vector-representation" : true, + "template-rendering-intent" : "template" + } +} diff --git a/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Filled/v2_settings_filled.imageset/settings_filled.svg b/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Filled/v2_settings_filled.imageset/settings_filled.svg new file mode 100644 index 00000000..0c58b0ee --- /dev/null +++ b/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Filled/v2_settings_filled.imageset/settings_filled.svg @@ -0,0 +1,3 @@ + + + diff --git a/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Filled/v2_star_filled.imageset/Contents.json b/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Filled/v2_star_filled.imageset/Contents.json new file mode 100644 index 00000000..9ea53472 --- /dev/null +++ b/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Filled/v2_star_filled.imageset/Contents.json @@ -0,0 +1,16 @@ +{ + "images" : [ + { + "filename" : "star_filled.svg", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "preserves-vector-representation" : true, + "template-rendering-intent" : "template" + } +} diff --git a/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Filled/v2_star_filled.imageset/star_filled.svg b/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Filled/v2_star_filled.imageset/star_filled.svg new file mode 100644 index 00000000..deee1483 --- /dev/null +++ b/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Filled/v2_star_filled.imageset/star_filled.svg @@ -0,0 +1,3 @@ + + + diff --git a/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Filled/v2_tag_filled.imageset/Contents.json b/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Filled/v2_tag_filled.imageset/Contents.json new file mode 100644 index 00000000..aef7b5f8 --- /dev/null +++ b/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Filled/v2_tag_filled.imageset/Contents.json @@ -0,0 +1,16 @@ +{ + "images" : [ + { + "filename" : "tag_filled.svg", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "preserves-vector-representation" : true, + "template-rendering-intent" : "template" + } +} diff --git a/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Filled/v2_tag_filled.imageset/tag_filled.svg b/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Filled/v2_tag_filled.imageset/tag_filled.svg new file mode 100644 index 00000000..d3093acd --- /dev/null +++ b/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Filled/v2_tag_filled.imageset/tag_filled.svg @@ -0,0 +1,4 @@ + + + + diff --git a/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Filled/v2_time_filled.imageset/Contents.json b/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Filled/v2_time_filled.imageset/Contents.json new file mode 100644 index 00000000..4160db24 --- /dev/null +++ b/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Filled/v2_time_filled.imageset/Contents.json @@ -0,0 +1,16 @@ +{ + "images" : [ + { + "filename" : "time_filled.svg", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "preserves-vector-representation" : true, + "template-rendering-intent" : "template" + } +} diff --git a/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Filled/v2_time_filled.imageset/time_filled.svg b/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Filled/v2_time_filled.imageset/time_filled.svg new file mode 100644 index 00000000..d7352164 --- /dev/null +++ b/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Filled/v2_time_filled.imageset/time_filled.svg @@ -0,0 +1,3 @@ + + + diff --git a/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Filled/v2_trash_filled.imageset/Contents.json b/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Filled/v2_trash_filled.imageset/Contents.json new file mode 100644 index 00000000..c5bad132 --- /dev/null +++ b/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Filled/v2_trash_filled.imageset/Contents.json @@ -0,0 +1,16 @@ +{ + "images" : [ + { + "filename" : "trash_filled.svg", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "preserves-vector-representation" : true, + "template-rendering-intent" : "template" + } +} diff --git a/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Filled/v2_trash_filled.imageset/trash_filled.svg b/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Filled/v2_trash_filled.imageset/trash_filled.svg new file mode 100644 index 00000000..e030089b --- /dev/null +++ b/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Filled/v2_trash_filled.imageset/trash_filled.svg @@ -0,0 +1,4 @@ + + + + diff --git a/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Filled/v2_user_filled.imageset/Contents.json b/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Filled/v2_user_filled.imageset/Contents.json new file mode 100644 index 00000000..032e3249 --- /dev/null +++ b/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Filled/v2_user_filled.imageset/Contents.json @@ -0,0 +1,16 @@ +{ + "images" : [ + { + "filename" : "user_filled.svg", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "preserves-vector-representation" : true, + "template-rendering-intent" : "template" + } +} diff --git a/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Filled/v2_user_filled.imageset/user_filled.svg b/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Filled/v2_user_filled.imageset/user_filled.svg new file mode 100644 index 00000000..72e3cee5 --- /dev/null +++ b/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Filled/v2_user_filled.imageset/user_filled.svg @@ -0,0 +1,4 @@ + + + + diff --git a/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Filled/v2_write_filled.imageset/Contents.json b/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Filled/v2_write_filled.imageset/Contents.json new file mode 100644 index 00000000..4295b2a5 --- /dev/null +++ b/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Filled/v2_write_filled.imageset/Contents.json @@ -0,0 +1,16 @@ +{ + "images" : [ + { + "filename" : "write_filled.svg", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "preserves-vector-representation" : true, + "template-rendering-intent" : "template" + } +} diff --git a/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Filled/v2_write_filled.imageset/write_filled.svg b/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Filled/v2_write_filled.imageset/write_filled.svg new file mode 100644 index 00000000..d4ada833 --- /dev/null +++ b/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Filled/v2_write_filled.imageset/write_filled.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Outlined/Contents.json b/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Outlined/Contents.json new file mode 100644 index 00000000..73c00596 --- /dev/null +++ b/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Outlined/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Outlined/v2_bell_outlined.imageset/Contents.json b/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Outlined/v2_bell_outlined.imageset/Contents.json new file mode 100644 index 00000000..ffad4de9 --- /dev/null +++ b/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Outlined/v2_bell_outlined.imageset/Contents.json @@ -0,0 +1,16 @@ +{ + "images" : [ + { + "filename" : "bell_outlined.svg", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "preserves-vector-representation" : true, + "template-rendering-intent" : "template" + } +} diff --git a/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Outlined/v2_bell_outlined.imageset/bell_outlined.svg b/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Outlined/v2_bell_outlined.imageset/bell_outlined.svg new file mode 100644 index 00000000..586c2bba --- /dev/null +++ b/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Outlined/v2_bell_outlined.imageset/bell_outlined.svg @@ -0,0 +1,4 @@ + + + + diff --git a/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Outlined/v2_camera_outlined.imageset/Contents.json b/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Outlined/v2_camera_outlined.imageset/Contents.json new file mode 100644 index 00000000..9d466ff4 --- /dev/null +++ b/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Outlined/v2_camera_outlined.imageset/Contents.json @@ -0,0 +1,16 @@ +{ + "images" : [ + { + "filename" : "camera_outlined.svg", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "preserves-vector-representation" : true, + "template-rendering-intent" : "template" + } +} diff --git a/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Outlined/v2_camera_outlined.imageset/camera_outlined.svg b/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Outlined/v2_camera_outlined.imageset/camera_outlined.svg new file mode 100644 index 00000000..73afffa0 --- /dev/null +++ b/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Outlined/v2_camera_outlined.imageset/camera_outlined.svg @@ -0,0 +1,4 @@ + + + + diff --git a/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Outlined/v2_check_outlined.imageset/Contents.json b/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Outlined/v2_check_outlined.imageset/Contents.json new file mode 100644 index 00000000..8ea481f6 --- /dev/null +++ b/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Outlined/v2_check_outlined.imageset/Contents.json @@ -0,0 +1,16 @@ +{ + "images" : [ + { + "filename" : "check_outlined.svg", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "preserves-vector-representation" : true, + "template-rendering-intent" : "template" + } +} diff --git a/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Outlined/v2_check_outlined.imageset/check_outlined.svg b/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Outlined/v2_check_outlined.imageset/check_outlined.svg new file mode 100644 index 00000000..d940ee8e --- /dev/null +++ b/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Outlined/v2_check_outlined.imageset/check_outlined.svg @@ -0,0 +1,3 @@ + + + diff --git a/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Outlined/v2_danger_outlined.imageset/Contents.json b/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Outlined/v2_danger_outlined.imageset/Contents.json new file mode 100644 index 00000000..76f2f52d --- /dev/null +++ b/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Outlined/v2_danger_outlined.imageset/Contents.json @@ -0,0 +1,16 @@ +{ + "images" : [ + { + "filename" : "danger_outlined.svg", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "preserves-vector-representation" : true, + "template-rendering-intent" : "template" + } +} diff --git a/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Outlined/v2_danger_outlined.imageset/danger_outlined.svg b/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Outlined/v2_danger_outlined.imageset/danger_outlined.svg new file mode 100644 index 00000000..e6022bed --- /dev/null +++ b/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Outlined/v2_danger_outlined.imageset/danger_outlined.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Outlined/v2_delete_outlined.imageset/Contents.json b/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Outlined/v2_delete_outlined.imageset/Contents.json new file mode 100644 index 00000000..0616df60 --- /dev/null +++ b/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Outlined/v2_delete_outlined.imageset/Contents.json @@ -0,0 +1,16 @@ +{ + "images" : [ + { + "filename" : "delete_outlined.svg", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "preserves-vector-representation" : true, + "template-rendering-intent" : "template" + } +} diff --git a/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Outlined/v2_delete_outlined.imageset/delete_outlined.svg b/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Outlined/v2_delete_outlined.imageset/delete_outlined.svg new file mode 100644 index 00000000..c6186d2f --- /dev/null +++ b/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Outlined/v2_delete_outlined.imageset/delete_outlined.svg @@ -0,0 +1,4 @@ + + + + diff --git a/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Outlined/v2_down_outlined.imageset/Contents.json b/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Outlined/v2_down_outlined.imageset/Contents.json new file mode 100644 index 00000000..13f6239e --- /dev/null +++ b/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Outlined/v2_down_outlined.imageset/Contents.json @@ -0,0 +1,16 @@ +{ + "images" : [ + { + "filename" : "down_outlined.svg", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "preserves-vector-representation" : true, + "template-rendering-intent" : "template" + } +} diff --git a/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Outlined/v2_down_outlined.imageset/down_outlined.svg b/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Outlined/v2_down_outlined.imageset/down_outlined.svg new file mode 100644 index 00000000..989a805f --- /dev/null +++ b/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Outlined/v2_down_outlined.imageset/down_outlined.svg @@ -0,0 +1,3 @@ + + + diff --git a/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Outlined/v2_error_outlined.imageset/Contents.json b/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Outlined/v2_error_outlined.imageset/Contents.json new file mode 100644 index 00000000..c7cae5ea --- /dev/null +++ b/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Outlined/v2_error_outlined.imageset/Contents.json @@ -0,0 +1,16 @@ +{ + "images" : [ + { + "filename" : "error_outlined.svg", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "preserves-vector-representation" : true, + "template-rendering-intent" : "template" + } +} diff --git a/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Outlined/v2_error_outlined.imageset/error_outlined.svg b/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Outlined/v2_error_outlined.imageset/error_outlined.svg new file mode 100644 index 00000000..5bd5874a --- /dev/null +++ b/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Outlined/v2_error_outlined.imageset/error_outlined.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Outlined/v2_heart_outlined.imageset/Contents.json b/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Outlined/v2_heart_outlined.imageset/Contents.json new file mode 100644 index 00000000..cf085279 --- /dev/null +++ b/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Outlined/v2_heart_outlined.imageset/Contents.json @@ -0,0 +1,16 @@ +{ + "images" : [ + { + "filename" : "heart_outlined.svg", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "preserves-vector-representation" : true, + "template-rendering-intent" : "template" + } +} diff --git a/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Outlined/v2_heart_outlined.imageset/heart_outlined.svg b/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Outlined/v2_heart_outlined.imageset/heart_outlined.svg new file mode 100644 index 00000000..693e0556 --- /dev/null +++ b/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Outlined/v2_heart_outlined.imageset/heart_outlined.svg @@ -0,0 +1,3 @@ + + + diff --git a/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Outlined/v2_home_outlined.imageset/Contents.json b/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Outlined/v2_home_outlined.imageset/Contents.json new file mode 100644 index 00000000..858581e7 --- /dev/null +++ b/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Outlined/v2_home_outlined.imageset/Contents.json @@ -0,0 +1,16 @@ +{ + "images" : [ + { + "filename" : "home_outlined.svg", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "preserves-vector-representation" : true, + "template-rendering-intent" : "template" + } +} diff --git a/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Outlined/v2_home_outlined.imageset/home_outlined.svg b/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Outlined/v2_home_outlined.imageset/home_outlined.svg new file mode 100644 index 00000000..8c2152ac --- /dev/null +++ b/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Outlined/v2_home_outlined.imageset/home_outlined.svg @@ -0,0 +1,3 @@ + + + diff --git a/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Outlined/v2_image_outlined.imageset/Contents.json b/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Outlined/v2_image_outlined.imageset/Contents.json new file mode 100644 index 00000000..09d24c07 --- /dev/null +++ b/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Outlined/v2_image_outlined.imageset/Contents.json @@ -0,0 +1,16 @@ +{ + "images" : [ + { + "filename" : "image_outlined.svg", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "preserves-vector-representation" : true, + "template-rendering-intent" : "template" + } +} diff --git a/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Outlined/v2_image_outlined.imageset/image_outlined.svg b/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Outlined/v2_image_outlined.imageset/image_outlined.svg new file mode 100644 index 00000000..e13eb67c --- /dev/null +++ b/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Outlined/v2_image_outlined.imageset/image_outlined.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Outlined/v2_left_outlined.imageset/Contents.json b/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Outlined/v2_left_outlined.imageset/Contents.json new file mode 100644 index 00000000..9dbd3c2c --- /dev/null +++ b/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Outlined/v2_left_outlined.imageset/Contents.json @@ -0,0 +1,16 @@ +{ + "images" : [ + { + "filename" : "left_outlined.svg", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "preserves-vector-representation" : true, + "template-rendering-intent" : "template" + } +} diff --git a/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Outlined/v2_left_outlined.imageset/left_outlined.svg b/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Outlined/v2_left_outlined.imageset/left_outlined.svg new file mode 100644 index 00000000..abe68d69 --- /dev/null +++ b/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Outlined/v2_left_outlined.imageset/left_outlined.svg @@ -0,0 +1,3 @@ + + + diff --git a/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Outlined/v2_location_outlined.imageset/Contents.json b/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Outlined/v2_location_outlined.imageset/Contents.json new file mode 100644 index 00000000..29d82ed7 --- /dev/null +++ b/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Outlined/v2_location_outlined.imageset/Contents.json @@ -0,0 +1,16 @@ +{ + "images" : [ + { + "filename" : "location_outlined.svg", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "preserves-vector-representation" : true, + "template-rendering-intent" : "template" + } +} diff --git a/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Outlined/v2_location_outlined.imageset/location_outlined.svg b/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Outlined/v2_location_outlined.imageset/location_outlined.svg new file mode 100644 index 00000000..37fe871e --- /dev/null +++ b/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Outlined/v2_location_outlined.imageset/location_outlined.svg @@ -0,0 +1,4 @@ + + + + diff --git a/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Outlined/v2_message_circle_outlined.imageset/Contents.json b/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Outlined/v2_message_circle_outlined.imageset/Contents.json new file mode 100644 index 00000000..465a538f --- /dev/null +++ b/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Outlined/v2_message_circle_outlined.imageset/Contents.json @@ -0,0 +1,16 @@ +{ + "images" : [ + { + "filename" : "message_circle_outlined.svg", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "preserves-vector-representation" : true, + "template-rendering-intent" : "template" + } +} diff --git a/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Outlined/v2_message_circle_outlined.imageset/message_circle_outlined.svg b/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Outlined/v2_message_circle_outlined.imageset/message_circle_outlined.svg new file mode 100644 index 00000000..4d32a4fc --- /dev/null +++ b/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Outlined/v2_message_circle_outlined.imageset/message_circle_outlined.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Outlined/v2_message_square_outlined.imageset/Contents.json b/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Outlined/v2_message_square_outlined.imageset/Contents.json new file mode 100644 index 00000000..4edcf321 --- /dev/null +++ b/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Outlined/v2_message_square_outlined.imageset/Contents.json @@ -0,0 +1,16 @@ +{ + "images" : [ + { + "filename" : "message_square_outlined.svg", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "preserves-vector-representation" : true, + "template-rendering-intent" : "template" + } +} diff --git a/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Outlined/v2_message_square_outlined.imageset/message_square_outlined.svg b/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Outlined/v2_message_square_outlined.imageset/message_square_outlined.svg new file mode 100644 index 00000000..8cd29b43 --- /dev/null +++ b/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Outlined/v2_message_square_outlined.imageset/message_square_outlined.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Outlined/v2_more_outlined.imageset/Contents.json b/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Outlined/v2_more_outlined.imageset/Contents.json new file mode 100644 index 00000000..ab571461 --- /dev/null +++ b/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Outlined/v2_more_outlined.imageset/Contents.json @@ -0,0 +1,16 @@ +{ + "images" : [ + { + "filename" : "more_outlined.svg", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "preserves-vector-representation" : true, + "template-rendering-intent" : "template" + } +} diff --git a/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Outlined/v2_more_outlined.imageset/more_outlined.svg b/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Outlined/v2_more_outlined.imageset/more_outlined.svg new file mode 100644 index 00000000..472c2047 --- /dev/null +++ b/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Outlined/v2_more_outlined.imageset/more_outlined.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Outlined/v2_plus_outlined.imageset/Contents.json b/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Outlined/v2_plus_outlined.imageset/Contents.json new file mode 100644 index 00000000..3f77fb15 --- /dev/null +++ b/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Outlined/v2_plus_outlined.imageset/Contents.json @@ -0,0 +1,16 @@ +{ + "images" : [ + { + "filename" : "plus_outlined.svg", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "preserves-vector-representation" : true, + "template-rendering-intent" : "template" + } +} diff --git a/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Outlined/v2_plus_outlined.imageset/plus_outlined.svg b/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Outlined/v2_plus_outlined.imageset/plus_outlined.svg new file mode 100644 index 00000000..f28f1c62 --- /dev/null +++ b/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Outlined/v2_plus_outlined.imageset/plus_outlined.svg @@ -0,0 +1,4 @@ + + + + diff --git a/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Outlined/v2_right_outlined.imageset/Contents.json b/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Outlined/v2_right_outlined.imageset/Contents.json new file mode 100644 index 00000000..711cb869 --- /dev/null +++ b/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Outlined/v2_right_outlined.imageset/Contents.json @@ -0,0 +1,16 @@ +{ + "images" : [ + { + "filename" : "right_outlined.svg", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "preserves-vector-representation" : true, + "template-rendering-intent" : "template" + } +} diff --git a/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Outlined/v2_right_outlined.imageset/right_outlined.svg b/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Outlined/v2_right_outlined.imageset/right_outlined.svg new file mode 100644 index 00000000..5acef760 --- /dev/null +++ b/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Outlined/v2_right_outlined.imageset/right_outlined.svg @@ -0,0 +1,3 @@ + + + diff --git a/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Outlined/v2_search_outlined.imageset/Contents.json b/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Outlined/v2_search_outlined.imageset/Contents.json new file mode 100644 index 00000000..f667080b --- /dev/null +++ b/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Outlined/v2_search_outlined.imageset/Contents.json @@ -0,0 +1,16 @@ +{ + "images" : [ + { + "filename" : "search_outlined.svg", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "preserves-vector-representation" : true, + "template-rendering-intent" : "template" + } +} diff --git a/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Outlined/v2_search_outlined.imageset/search_outlined.svg b/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Outlined/v2_search_outlined.imageset/search_outlined.svg new file mode 100644 index 00000000..79467171 --- /dev/null +++ b/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Outlined/v2_search_outlined.imageset/search_outlined.svg @@ -0,0 +1,4 @@ + + + + diff --git a/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Outlined/v2_settings_outlined.imageset/Contents.json b/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Outlined/v2_settings_outlined.imageset/Contents.json new file mode 100644 index 00000000..3023f1a2 --- /dev/null +++ b/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Outlined/v2_settings_outlined.imageset/Contents.json @@ -0,0 +1,16 @@ +{ + "images" : [ + { + "filename" : "settings_outlined.svg", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "preserves-vector-representation" : true, + "template-rendering-intent" : "template" + } +} diff --git a/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Outlined/v2_settings_outlined.imageset/settings_outlined.svg b/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Outlined/v2_settings_outlined.imageset/settings_outlined.svg new file mode 100644 index 00000000..655a31e2 --- /dev/null +++ b/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Outlined/v2_settings_outlined.imageset/settings_outlined.svg @@ -0,0 +1,4 @@ + + + + diff --git a/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Outlined/v2_star_outlined.imageset/Contents.json b/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Outlined/v2_star_outlined.imageset/Contents.json new file mode 100644 index 00000000..6ef99b3b --- /dev/null +++ b/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Outlined/v2_star_outlined.imageset/Contents.json @@ -0,0 +1,16 @@ +{ + "images" : [ + { + "filename" : "star_outlined.svg", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "preserves-vector-representation" : true, + "template-rendering-intent" : "template" + } +} diff --git a/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Outlined/v2_star_outlined.imageset/star_outlined.svg b/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Outlined/v2_star_outlined.imageset/star_outlined.svg new file mode 100644 index 00000000..e1d88da9 --- /dev/null +++ b/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Outlined/v2_star_outlined.imageset/star_outlined.svg @@ -0,0 +1,3 @@ + + + diff --git a/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Outlined/v2_swap_outlined.imageset/Contents.json b/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Outlined/v2_swap_outlined.imageset/Contents.json new file mode 100644 index 00000000..861d0d59 --- /dev/null +++ b/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Outlined/v2_swap_outlined.imageset/Contents.json @@ -0,0 +1,16 @@ +{ + "images" : [ + { + "filename" : "swap_outlined.svg", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "preserves-vector-representation" : true, + "template-rendering-intent" : "template" + } +} diff --git a/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Outlined/v2_swap_outlined.imageset/swap_outlined.svg b/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Outlined/v2_swap_outlined.imageset/swap_outlined.svg new file mode 100644 index 00000000..61b64fcc --- /dev/null +++ b/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Outlined/v2_swap_outlined.imageset/swap_outlined.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Outlined/v2_tag_outlined.imageset/Contents.json b/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Outlined/v2_tag_outlined.imageset/Contents.json new file mode 100644 index 00000000..0760205b --- /dev/null +++ b/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Outlined/v2_tag_outlined.imageset/Contents.json @@ -0,0 +1,16 @@ +{ + "images" : [ + { + "filename" : "tag_outlined.svg", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "preserves-vector-representation" : true, + "template-rendering-intent" : "template" + } +} diff --git a/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Outlined/v2_tag_outlined.imageset/tag_outlined.svg b/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Outlined/v2_tag_outlined.imageset/tag_outlined.svg new file mode 100644 index 00000000..7d69d7d0 --- /dev/null +++ b/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Outlined/v2_tag_outlined.imageset/tag_outlined.svg @@ -0,0 +1,4 @@ + + + + diff --git a/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Outlined/v2_time_outlined.imageset/Contents.json b/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Outlined/v2_time_outlined.imageset/Contents.json new file mode 100644 index 00000000..e6d5843a --- /dev/null +++ b/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Outlined/v2_time_outlined.imageset/Contents.json @@ -0,0 +1,16 @@ +{ + "images" : [ + { + "filename" : "time_outlined.svg", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "preserves-vector-representation" : true, + "template-rendering-intent" : "template" + } +} diff --git a/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Outlined/v2_time_outlined.imageset/time_outlined.svg b/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Outlined/v2_time_outlined.imageset/time_outlined.svg new file mode 100644 index 00000000..e7b1e124 --- /dev/null +++ b/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Outlined/v2_time_outlined.imageset/time_outlined.svg @@ -0,0 +1,4 @@ + + + + diff --git a/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Outlined/v2_trash_outlined.imageset/Contents.json b/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Outlined/v2_trash_outlined.imageset/Contents.json new file mode 100644 index 00000000..f7490657 --- /dev/null +++ b/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Outlined/v2_trash_outlined.imageset/Contents.json @@ -0,0 +1,16 @@ +{ + "images" : [ + { + "filename" : "trash_outlined.svg", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "preserves-vector-representation" : true, + "template-rendering-intent" : "template" + } +} diff --git a/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Outlined/v2_trash_outlined.imageset/trash_outlined.svg b/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Outlined/v2_trash_outlined.imageset/trash_outlined.svg new file mode 100644 index 00000000..8e652075 --- /dev/null +++ b/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Outlined/v2_trash_outlined.imageset/trash_outlined.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Outlined/v2_up_outlined.imageset/Contents.json b/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Outlined/v2_up_outlined.imageset/Contents.json new file mode 100644 index 00000000..a379f110 --- /dev/null +++ b/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Outlined/v2_up_outlined.imageset/Contents.json @@ -0,0 +1,16 @@ +{ + "images" : [ + { + "filename" : "up_outlined.svg", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "preserves-vector-representation" : true, + "template-rendering-intent" : "template" + } +} diff --git a/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Outlined/v2_up_outlined.imageset/up_outlined.svg b/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Outlined/v2_up_outlined.imageset/up_outlined.svg new file mode 100644 index 00000000..b1e4f4e3 --- /dev/null +++ b/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Outlined/v2_up_outlined.imageset/up_outlined.svg @@ -0,0 +1,3 @@ + + + diff --git a/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Outlined/v2_user_outlined.imageset/Contents.json b/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Outlined/v2_user_outlined.imageset/Contents.json new file mode 100644 index 00000000..32ae4010 --- /dev/null +++ b/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Outlined/v2_user_outlined.imageset/Contents.json @@ -0,0 +1,16 @@ +{ + "images" : [ + { + "filename" : "user_outlined.svg", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "preserves-vector-representation" : true, + "template-rendering-intent" : "template" + } +} diff --git a/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Outlined/v2_user_outlined.imageset/user_outlined.svg b/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Outlined/v2_user_outlined.imageset/user_outlined.svg new file mode 100644 index 00000000..58bfe2d2 --- /dev/null +++ b/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Outlined/v2_user_outlined.imageset/user_outlined.svg @@ -0,0 +1,4 @@ + + + + diff --git a/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Outlined/v2_write_outlined.imageset/Contents.json b/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Outlined/v2_write_outlined.imageset/Contents.json new file mode 100644 index 00000000..50bf40af --- /dev/null +++ b/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Outlined/v2_write_outlined.imageset/Contents.json @@ -0,0 +1,16 @@ +{ + "images" : [ + { + "filename" : "write_outlined.svg", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "preserves-vector-representation" : true, + "template-rendering-intent" : "template" + } +} diff --git a/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Outlined/v2_write_outlined.imageset/write_outlined.svg b/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Outlined/v2_write_outlined.imageset/write_outlined.svg new file mode 100644 index 00000000..a33731db --- /dev/null +++ b/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Icons/Outlined/v2_write_outlined.imageset/write_outlined.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Images/Contents.json b/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Images/Contents.json new file mode 100644 index 00000000..73c00596 --- /dev/null +++ b/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Images/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Images/v2_check_square_light.imageset/Contents.json b/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Images/v2_check_square_light.imageset/Contents.json new file mode 100644 index 00000000..455aa1f2 --- /dev/null +++ b/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Images/v2_check_square_light.imageset/Contents.json @@ -0,0 +1,16 @@ +{ + "images" : [ + { + "filename" : "check_square_light.svg", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "preserves-vector-representation" : true, + "template-rendering-intent" : "original" + } +} diff --git a/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Images/v2_check_square_light.imageset/check_square_light.svg b/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Images/v2_check_square_light.imageset/check_square_light.svg new file mode 100644 index 00000000..71c2689f --- /dev/null +++ b/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Images/v2_check_square_light.imageset/check_square_light.svg @@ -0,0 +1,4 @@ + + + + diff --git a/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Images/v2_onboarding.imageset/Contents.json b/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Images/v2_onboarding.imageset/Contents.json new file mode 100644 index 00000000..9042d343 --- /dev/null +++ b/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Images/v2_onboarding.imageset/Contents.json @@ -0,0 +1,16 @@ +{ + "images" : [ + { + "filename" : "onboarding.svg", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "preserves-vector-representation" : true, + "template-rendering-intent" : "original" + } +} diff --git a/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Images/v2_onboarding.imageset/onboarding.svg b/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Images/v2_onboarding.imageset/onboarding.svg new file mode 100644 index 00000000..84c304e7 --- /dev/null +++ b/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Images/v2_onboarding.imageset/onboarding.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Images/v2_onboarding_finish.imageset/Contents.json b/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Images/v2_onboarding_finish.imageset/Contents.json new file mode 100644 index 00000000..1ef0aae8 --- /dev/null +++ b/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Images/v2_onboarding_finish.imageset/Contents.json @@ -0,0 +1,16 @@ +{ + "images" : [ + { + "filename" : "onboarding_finish.svg", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "preserves-vector-representation" : true, + "template-rendering-intent" : "original" + } +} diff --git a/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Images/v2_onboarding_finish.imageset/onboarding_finish.svg b/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Images/v2_onboarding_finish.imageset/onboarding_finish.svg new file mode 100644 index 00000000..eecb62f3 --- /dev/null +++ b/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Images/v2_onboarding_finish.imageset/onboarding_finish.svg @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Images/v2_profile.imageset/Contents.json b/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Images/v2_profile.imageset/Contents.json new file mode 100644 index 00000000..257a53bb --- /dev/null +++ b/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Images/v2_profile.imageset/Contents.json @@ -0,0 +1,16 @@ +{ + "images" : [ + { + "filename" : "profile.svg", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "preserves-vector-representation" : true, + "template-rendering-intent" : "original" + } +} diff --git a/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Images/v2_profile.imageset/profile.svg b/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Images/v2_profile.imageset/profile.svg new file mode 100644 index 00000000..7affdc14 --- /dev/null +++ b/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Images/v2_profile.imageset/profile.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Logos/Contents.json b/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Logos/Contents.json new file mode 100644 index 00000000..73c00596 --- /dev/null +++ b/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Logos/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Logos/v2_logo_white.imageset/Contents.json b/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Logos/v2_logo_white.imageset/Contents.json new file mode 100644 index 00000000..c25e3ab6 --- /dev/null +++ b/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Logos/v2_logo_white.imageset/Contents.json @@ -0,0 +1,16 @@ +{ + "images" : [ + { + "filename" : "logo_white.svg", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "preserves-vector-representation" : true, + "template-rendering-intent" : "original" + } +} diff --git a/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Logos/v2_logo_white.imageset/logo_white.svg b/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Logos/v2_logo_white.imageset/logo_white.svg new file mode 100644 index 00000000..e9ed230c --- /dev/null +++ b/SOOUM/SOOUM/Resources/Assets.xcassets/DesignSystem/V2/Logos/v2_logo_white.imageset/logo_white.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/SOOUM/SOOUM/Resources/Develop/Info-dev.plist b/SOOUM/SOOUM/Resources/Develop/Info-dev.plist index 1d47e318..d59b4811 100644 --- a/SOOUM/SOOUM/Resources/Develop/Info-dev.plist +++ b/SOOUM/SOOUM/Resources/Develop/Info-dev.plist @@ -2,27 +2,10 @@ - NSAppTransportSecurity - - NSExceptionDomains - - ec2-52-79-234-222.ap-northeast-2.compute.amazonaws.com - - NSExceptionAllowsInsecureHTTPLoads - - NSExceptionRequiresForwardSecrecy - - NSIncludesSubdomains - - NSTemporaryExceptionMinimumTLSVersion - TLSv1.2 - - - - ClarityId - $(SOOUM_CLARITY_ID) AppId $(SOOUM_APP_ID) + ClarityId + $(SOOUM_CLARITY_ID) ServerEndpoint $(SOOUM_SERVER_ENDPOINT) UIAppFonts diff --git a/SOOUM/SOOUM/Resources/Hakgyoansim-Bold.ttf b/SOOUM/SOOUM/Resources/Font/Hakgyoansim-Bold.ttf similarity index 100% rename from SOOUM/SOOUM/Resources/Hakgyoansim-Bold.ttf rename to SOOUM/SOOUM/Resources/Font/Hakgyoansim-Bold.ttf diff --git a/SOOUM/SOOUM/Resources/Hakgyoansim-Light.ttf b/SOOUM/SOOUM/Resources/Font/Hakgyoansim-Light.ttf similarity index 100% rename from SOOUM/SOOUM/Resources/Hakgyoansim-Light.ttf rename to SOOUM/SOOUM/Resources/Font/Hakgyoansim-Light.ttf diff --git a/SOOUM/SOOUM/Resources/PretendardVariable.ttf b/SOOUM/SOOUM/Resources/Font/PretendardVariable.ttf similarity index 100% rename from SOOUM/SOOUM/Resources/PretendardVariable.ttf rename to SOOUM/SOOUM/Resources/Font/PretendardVariable.ttf diff --git a/SOOUM/SOOUM/Resources/loading_indicator_lottie.json b/SOOUM/SOOUM/Resources/loading_indicator_lottie.json new file mode 100644 index 00000000..ee0160b0 --- /dev/null +++ b/SOOUM/SOOUM/Resources/loading_indicator_lottie.json @@ -0,0 +1 @@ +{"nm":"Main Scene","ddd":0,"h":60,"w":60,"meta":{"g":"@lottiefiles/creator 1.47.3"},"layers":[{"ty":4,"nm":"Shape Layer 1","sr":1,"st":1,"op":33,"ip":0,"hd":false,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[39.30069732666017,-18.2509729862213]},"s":{"a":0,"k":[40,40]},"sk":{"a":0,"k":0},"p":{"a":0,"k":[45.720278930664065,22.69961080551148]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}},"shapes":[{"ty":"el","bm":0,"hd":false,"mn":"ADBE Vector Shape - Ellipse","nm":"Ellipse Path 1","d":1,"p":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]}},{"ty":"tm","bm":0,"hd":false,"mn":"ADBE Vector Filter - Trim","nm":"Trim Paths 1","ix":2,"e":{"a":1,"k":[{"o":{"x":0.561,"y":0.016},"i":{"x":0.439,"y":1.017},"s":[100],"t":-1.00000004073083},{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[1],"t":0},{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[1],"t":1},{"o":{"x":0.167,"y":0.167},"i":{"x":0.833,"y":0.833},"s":[15],"t":6},{"s":[100],"t":28}],"ix":2},"o":{"a":0,"k":0,"ix":3},"s":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":0.68,"y":0.19},"s":[0],"t":0},{"o":{"x":0.55,"y":0.06},"i":{"x":1,"y":1},"s":[0],"t":6},{"o":{"x":0,"y":0},"i":{"x":0.36,"y":1},"s":[90],"t":28},{"s":[99],"t":33}],"ix":1},"m":1},{"ty":"st","bm":0,"hd":false,"mn":"ADBE Vector Graphic - Stroke","nm":"Stroke 1","lc":2,"lj":1,"ml":4,"o":{"a":0,"k":100},"w":{"a":0,"k":16},"c":{"a":0,"k":[0.1255,0.7765,0.9255]}}],"ind":1}],"v":"5.7.0","fr":29.9700012207031,"op":33,"ip":0,"assets":[]} \ No newline at end of file diff --git a/SOOUM/SOOUM/Utilities/Alamofire/Alamofire_Request.swift b/SOOUM/SOOUM/Utilities/Alamofire/Alamofire_Request.swift index f333b542..1af256ed 100644 --- a/SOOUM/SOOUM/Utilities/Alamofire/Alamofire_Request.swift +++ b/SOOUM/SOOUM/Utilities/Alamofire/Alamofire_Request.swift @@ -17,12 +17,6 @@ protocol BaseRequest: URLRequestConvertible { var parameters: Parameters { get } var encoding: ParameterEncoding { get } var authorizationType: AuthorizationType { get } - var version: APIVersion { get } -} - -enum APIVersion: String { - case v1 = "" - case v2 = "/v2" } enum AuthorizationType: String { diff --git a/SOOUM/SOOUM/Utilities/Alamofire/Alamofire_constants.swift b/SOOUM/SOOUM/Utilities/Alamofire/Alamofire_constants.swift index 06e12a11..5bc67984 100644 --- a/SOOUM/SOOUM/Utilities/Alamofire/Alamofire_constants.swift +++ b/SOOUM/SOOUM/Utilities/Alamofire/Alamofire_constants.swift @@ -15,11 +15,7 @@ struct Constants { } static var endpoint: String { - #if DEVELOP - return self.serverEndpoint(scheme: "http://") - #elseif PRODUCTION return self.serverEndpoint(scheme: "https://") - #endif } enum HTTPHeader: String { diff --git a/SOOUM/SOOUM/Utilities/SwiftEntryKit/SwiftEntryKit.swift b/SOOUM/SOOUM/Utilities/SwiftEntryKit/SwiftEntryKit.swift new file mode 100644 index 00000000..3c010817 --- /dev/null +++ b/SOOUM/SOOUM/Utilities/SwiftEntryKit/SwiftEntryKit.swift @@ -0,0 +1,36 @@ +// +// SwiftEntryKit.swift +// SOOUM +// +// Created by 오현식 on 9/12/25. +// + +import UIKit + +import SwiftEntryKit + +protocol SwiftEntryKitExtension { + + var entryName: String? { get } + + /// *주의 : 반드시 display 직전에 올바른 entryName을 지정해야 함 + func show(with attributes: EKAttributes) + + func dismiss(_ completion: (() -> Void)?) + static func dismiss(_ completion: (() -> Void)?) +} + +extension SwiftEntryKitExtension { + + func dismiss(_ completion: (() -> Void)? = nil) { + if let entryName = self.entryName { + SwiftEntryKit.dismiss(.specific(entryName: entryName), with: completion) + } else { + Self.dismiss(completion) + } + } + + static func dismiss(_ completion: (() -> Void)? = nil) { + SwiftEntryKit.dismiss(.displayed, with: completion) + } +} diff --git a/SOOUM/SOOUM/Utilities/SwiftEntryKit/View+SwiftEntryKit.swift b/SOOUM/SOOUM/Utilities/SwiftEntryKit/View+SwiftEntryKit.swift new file mode 100644 index 00000000..a08f14ed --- /dev/null +++ b/SOOUM/SOOUM/Utilities/SwiftEntryKit/View+SwiftEntryKit.swift @@ -0,0 +1,93 @@ +// +// View+SwiftEntryKit.swift +// SOOUM +// +// Created by 오현식 on 9/12/25. +// + +import UIKit + +import SwiftEntryKit + +protocol SwiftEntryKitViewExtension: SwiftEntryKitExtension { + var afterView: () -> UIView? { get } +} + +protocol SwiftEntryKitViewBridge { + associatedtype Base + var sek: SwiftEntryKitViewWrapper { get } +} + +struct SwiftEntryKitViewWrapper: SwiftEntryKitViewExtension { + + var afterView: () -> UIView? + var entryName: String? + + init(closure: @escaping () -> UIView?) { + self.afterView = closure + } +} + +extension SwiftEntryKitViewExtension { + + func show(with attributes: EKAttributes) { + guard let view = self.afterView() else { return } + var attributes: EKAttributes = attributes + attributes.name = self.entryName + DispatchQueue.main.async { + SwiftEntryKit.display(entry: view, using: attributes) + } + } +} + +extension UIView: SwiftEntryKitViewBridge { + + var sek: SwiftEntryKitViewWrapper { + return .init { [weak self] in self } + } +} + +extension SwiftEntryKitViewWrapper where Base == UIView { + + func showBottomFloat( + screenColor: UIColor? = .som.v2.dim, + screenInteraction: EKAttributes.UserInteraction, + useSafeArea: Bool = true, + hasHandleBar: Bool = true, + workAtWillAppear: (() -> Void)? = nil, + completion: (() -> Void)? = nil + ) { + var attributes: EKAttributes = .bottomFloat + + if useSafeArea { + attributes.positionConstraints.safeArea = .overridden + } + + if let screenColor: UIColor = screenColor { + attributes.screenBackground = .color(color: .init(screenColor)) + } else { + attributes.screenBackground = .clear + } + + if hasHandleBar { + attributes.scroll = .edgeCrossingDisabled(swipeable: true) + } + + attributes.roundCorners = .all(radius: 20) + attributes.positionConstraints.verticalOffset = 34 + + attributes.entryBackground = .color(color: .init(.som.v2.white)) + + attributes.displayDuration = .infinity + attributes.entranceAnimation = .init(translate: .init(duration: 0.25)) + attributes.exitAnimation = .init(translate: .init(duration: 0.25)) + + attributes.entryInteraction = .forward + attributes.screenInteraction = screenInteraction + + attributes.lifecycleEvents.willAppear = workAtWillAppear + attributes.lifecycleEvents.willDisappear = completion + + self.show(with: attributes) + } +}