├── Pyonsnal-Color ├── .swiftlint.yml ├── Pyonsnal-Color │ ├── Resource │ │ ├── Assets.xcassets │ │ │ ├── Contents.json │ │ │ ├── Common │ │ │ │ ├── Contents.json │ │ │ │ ├── check.imageset │ │ │ │ │ ├── check.svg │ │ │ │ │ └── Contents.json │ │ │ │ ├── icon_close.imageset │ │ │ │ │ ├── icon_close.svg │ │ │ │ │ └── Contents.json │ │ │ │ ├── icon_title.imageset │ │ │ │ │ └── Contents.json │ │ │ │ ├── icon_arrow.imageset │ │ │ │ │ ├── Contents.json │ │ │ │ │ └── icon_detail.svg │ │ │ │ ├── check.selected.imageset │ │ │ │ │ ├── Contents.json │ │ │ │ │ └── check.selected.svg │ │ │ │ ├── check.selected.red.imageset │ │ │ │ │ ├── Contents.json │ │ │ │ │ └── icon_check_selected.svg │ │ │ │ ├── icon_like_selected.imageset │ │ │ │ │ ├── Contents.json │ │ │ │ │ └── icon_like_selected.svg │ │ │ │ └── icon_like_unselected.imageset │ │ │ │ │ ├── Contents.json │ │ │ │ │ └── icon_like_unselected.svg │ │ │ ├── Filter │ │ │ │ ├── Contents.json │ │ │ │ ├── icon_filter.imageset │ │ │ │ │ ├── Contents.json │ │ │ │ │ └── icon_filter.svg │ │ │ │ ├── icon_filter_refresh.imageset │ │ │ │ │ ├── Contents.json │ │ │ │ │ └── icon_revert.svg │ │ │ │ └── icon_filter_view.imageset │ │ │ │ │ ├── Contents.json │ │ │ │ │ └── icon_filter_view.svg │ │ │ ├── Icon │ │ │ │ ├── Contents.json │ │ │ │ ├── logo.imageset │ │ │ │ │ ├── logo.png │ │ │ │ │ ├── logo@2x.png │ │ │ │ │ ├── logo@3x.png │ │ │ │ │ └── Contents.json │ │ │ │ ├── icon_header.imageset │ │ │ │ │ ├── Frame 8907.png │ │ │ │ │ ├── Frame 8907@2x.png │ │ │ │ │ ├── Frame 8907@3x.png │ │ │ │ │ └── Contents.json │ │ │ │ ├── icon_search.imageset │ │ │ │ │ ├── icon_search.png │ │ │ │ │ ├── icon_search@2x.png │ │ │ │ │ ├── icon_search@3x.png │ │ │ │ │ └── Contents.json │ │ │ │ ├── icon_pyonsnalColor.imageset │ │ │ │ │ ├── icon_pyonsnalColor.png │ │ │ │ │ ├── icon_pyonsnalColor@2x.png │ │ │ │ │ ├── icon_pyonsnalColor@3x.png │ │ │ │ │ └── Contents.json │ │ │ │ ├── icon_check.imageset │ │ │ │ │ ├── Contents.json │ │ │ │ │ └── icon_check.svg │ │ │ │ ├── icon_delete.imageset │ │ │ │ │ ├── Contents.json │ │ │ │ │ └── icon_delete.svg │ │ │ │ └── icon_delete_medium.imageset │ │ │ │ │ ├── Contents.json │ │ │ │ │ └── icon_delete(m).svg │ │ │ ├── Others │ │ │ │ ├── Contents.json │ │ │ │ ├── icon_delete.imageset │ │ │ │ │ ├── Contents.json │ │ │ │ │ └── icon_delete.svg │ │ │ │ └── icon_revert.imageset │ │ │ │ │ ├── Contents.json │ │ │ │ │ └── icon_revert.svg │ │ │ ├── Profile │ │ │ │ ├── Contents.json │ │ │ │ ├── profile_edit.imageset │ │ │ │ │ ├── edit_24.png │ │ │ │ │ ├── edit_24@2x.png │ │ │ │ │ ├── edit_24@3x.png │ │ │ │ │ └── Contents.json │ │ │ │ └── profile_plus.imageset │ │ │ │ │ ├── profile_plus@3x.png │ │ │ │ │ └── Contents.json │ │ │ ├── Review │ │ │ │ ├── Contents.json │ │ │ │ ├── icon_review.imageset │ │ │ │ │ ├── Contents.json │ │ │ │ │ └── icon_review.svg │ │ │ │ ├── star_large.imageset │ │ │ │ │ ├── Contents.json │ │ │ │ │ └── filled=unfilled, size=L.svg │ │ │ │ ├── star_medium.imageset │ │ │ │ │ ├── Contents.json │ │ │ │ │ └── filled=unfilled, size=m.svg │ │ │ │ ├── star_filled_large.imageset │ │ │ │ │ ├── Contents.json │ │ │ │ │ └── filled=filled, size=L.svg │ │ │ │ ├── star_filled_medium.imageset │ │ │ │ │ ├── Contents.json │ │ │ │ │ └── filled=filled, size=m.svg │ │ │ │ ├── icon_thumbsDown_filled.imageset │ │ │ │ │ ├── Contents.json │ │ │ │ │ └── icon_thumbsDown_filled.svg │ │ │ │ ├── icon_thumbsUp_filled.imageset │ │ │ │ │ ├── Contents.json │ │ │ │ │ └── icon_thumbsUp_filled.svg │ │ │ │ ├── icon_thumbsUp_unfilled.imageset │ │ │ │ │ ├── Contents.json │ │ │ │ │ └── icon_thumbsUp_unfilled.svg │ │ │ │ └── icon_thumbsDown_unfilled.imageset │ │ │ │ │ ├── Contents.json │ │ │ │ │ └── icon_thumbsDown_unfilled.svg │ │ │ ├── Splash │ │ │ │ ├── Contents.json │ │ │ │ └── image_splash.imageset │ │ │ │ │ ├── image_splash.png │ │ │ │ │ ├── image_splash@2x.png │ │ │ │ │ ├── image_splash@3x.png │ │ │ │ │ └── Contents.json │ │ │ ├── TabBar │ │ │ │ ├── Contents.json │ │ │ │ ├── icon_tab_my_selected.imageset │ │ │ │ │ ├── Contents.json │ │ │ │ │ └── icon_tab_my_selected.svg │ │ │ │ ├── icon_tab_home_selected.imageset │ │ │ │ │ └── Contents.json │ │ │ │ ├── icon_tab_my_unselected.imageset │ │ │ │ │ ├── Contents.json │ │ │ │ │ └── icon_tab_my_unselected.svg │ │ │ │ ├── icon_tab_sale_selected.imageset │ │ │ │ │ └── Contents.json │ │ │ │ ├── icon_tab_home_unselected.imageset │ │ │ │ │ └── Contents.json │ │ │ │ ├── icon_tab_sale_unselected.imageset │ │ │ │ │ ├── Contents.json │ │ │ │ │ └── icon_tab_sale_unselected.svg │ │ │ │ ├── icon_tab_favorite_selected.imageset │ │ │ │ │ ├── Contents.json │ │ │ │ │ └── icon_tab_selected.svg │ │ │ │ └── icon_tab_favorite_unselected.imageset │ │ │ │ │ ├── Contents.json │ │ │ │ │ └── icon_tab_unselected.svg │ │ │ ├── login │ │ │ │ ├── Contents.json │ │ │ │ ├── login_apple.imageset │ │ │ │ │ └── Contents.json │ │ │ │ └── login_kakao.imageset │ │ │ │ │ └── Contents.json │ │ │ ├── HeaderView │ │ │ │ ├── Contents.json │ │ │ │ ├── icon_back.imageset │ │ │ │ │ ├── Contents.json │ │ │ │ │ └── icon_back.svg │ │ │ │ └── bellSimple.imageset │ │ │ │ │ ├── Contents.json │ │ │ │ │ └── BellSimple.svg │ │ │ ├── ServiceTag │ │ │ │ ├── Contents.json │ │ │ │ └── tag_store.imageset │ │ │ │ │ ├── tag_store.png │ │ │ │ │ └── Contents.json │ │ │ ├── StoreLogo │ │ │ │ ├── Contents.json │ │ │ │ ├── store_logo_CU.imageset │ │ │ │ │ ├── store logo_CU.png │ │ │ │ │ └── Contents.json │ │ │ │ ├── store_logo_GS.imageset │ │ │ │ │ ├── store logo_GS.png │ │ │ │ │ └── Contents.json │ │ │ │ ├── store_logo_emart24.imageset │ │ │ │ │ ├── store logo_emart24.png │ │ │ │ │ └── Contents.json │ │ │ │ └── store_logo_7-eleven.imageset │ │ │ │ │ ├── store logo_GS_7-eleven.png │ │ │ │ │ └── Contents.json │ │ │ ├── StoreTag │ │ │ │ ├── Contents.json │ │ │ │ ├── tag_store_CU.imageset │ │ │ │ │ ├── tag_store_CU.png │ │ │ │ │ └── Contents.json │ │ │ │ ├── tag_store_GS.imageset │ │ │ │ │ ├── tag_store_GS.png │ │ │ │ │ └── Contents.json │ │ │ │ ├── tag_store_7eleven.imageset │ │ │ │ │ ├── tag_store_7eleven.png │ │ │ │ │ └── Contents.json │ │ │ │ └── tag_store_emart24.imageset │ │ │ │ │ ├── tag_store_emart24.png │ │ │ │ │ └── Contents.json │ │ │ ├── LineIconLarge │ │ │ │ ├── Contents.json │ │ │ │ └── icon_share.imageset │ │ │ │ │ ├── Contents.json │ │ │ │ │ └── icon_share.svg │ │ │ ├── LineIconMedium │ │ │ │ ├── Contents.json │ │ │ │ ├── icon_close.imageset │ │ │ │ │ ├── icon_close.svg │ │ │ │ │ └── Contents.json │ │ │ │ ├── icon_back.imageset │ │ │ │ │ ├── Contents.json │ │ │ │ │ └── icon_back.svg │ │ │ │ ├── icon_next.imageset │ │ │ │ │ ├── Contents.json │ │ │ │ │ └── icon_next.svg │ │ │ │ ├── icon_alarm.imageset │ │ │ │ │ ├── Contents.json │ │ │ │ │ └── icon_alarm.svg │ │ │ │ └── icon_check.imageset │ │ │ │ │ ├── Contents.json │ │ │ │ │ └── icon_check.svg │ │ │ ├── LineIconSmall │ │ │ │ ├── Contents.json │ │ │ │ ├── icon_close_small.imageset │ │ │ │ │ ├── icon_close_small.svg │ │ │ │ │ └── Contents.json │ │ │ │ ├── icon_detail.imageset │ │ │ │ │ ├── Contents.json │ │ │ │ │ └── icon_detail.svg │ │ │ │ ├── icon_filter.imageset │ │ │ │ │ ├── Contents.json │ │ │ │ │ └── icon_filter.svg │ │ │ │ └── icon_filter_view.imageset │ │ │ │ │ ├── Contents.json │ │ │ │ │ └── icon_filter_view.svg │ │ │ ├── AppIcon.appiconset │ │ │ │ ├── AppIcon.png │ │ │ │ └── Contents.json │ │ │ └── AccentColor.colorset │ │ │ │ └── Contents.json │ │ ├── Font │ │ │ ├── Pretendard-Medium.otf │ │ │ ├── Pretendard-Regular.otf │ │ │ └── Pretendard-SemiBold.otf │ │ ├── Constants │ │ │ ├── Url │ │ │ │ ├── ProfileUrl.swift │ │ │ │ └── TermsOfUseUrl.swift │ │ │ └── CommonConstants.swift │ │ ├── ImageAssetKind │ │ │ ├── ImageAssetKind+Splash.swift │ │ │ ├── ImageAssetKind+HeaderView.swift │ │ │ ├── ImageAssetKind+Profile.swift │ │ │ ├── ImageAsset+Filter.swift │ │ │ ├── ImageAssetKind+StoreTag.swift │ │ │ ├── ImageAssetKind+StoreIcon.swift │ │ │ ├── ImageAssetKind+TabBar.swift │ │ │ ├── ImageAssetKind+Icon.swift │ │ │ └── ImageAssetKind.swift │ │ └── GoogleService-Info.plist │ ├── Entity │ │ ├── Common │ │ │ ├── EmptyResponse.swift │ │ │ ├── ProductType.swift │ │ │ └── ErrorResponse.swift │ │ ├── Review │ │ │ ├── ReviewFeedKind.swift │ │ │ ├── UserReviewInput.swift │ │ │ ├── ReviewEvaluationState.swift │ │ │ ├── ReviewHateCountEntity.swift │ │ │ ├── ReviewLikeCountEntity.swift │ │ │ ├── ReviewEvaluationProtocol.swift │ │ │ ├── ReviewTasteEvaluation.swift │ │ │ ├── ReviewQualityEvaluation.swift │ │ │ ├── ReviewValueForMoneyEvaluation.swift │ │ │ ├── ReviewUploadEntity.swift │ │ │ └── ReviewEvaluationKind.swift │ │ ├── Auth │ │ │ ├── AuthType.swift │ │ │ ├── UserLoginStatusEntity.swift │ │ │ └── UserAuthEntity.swift │ │ ├── Member │ │ │ ├── NicknameEntity.swift │ │ │ └── MemberInfoEntity.swift │ │ ├── Products │ │ │ ├── ProductDetailSection.swift │ │ │ ├── ProductGiftEntity.swift │ │ │ ├── ProductDetailSectionModel.swift │ │ │ ├── SortEntity.swift │ │ │ ├── ProductDetailSectionItem.swift │ │ │ ├── PageableEntity.swift │ │ │ ├── EventBannerEntity.swift │ │ │ ├── ProductConvertable.swift │ │ │ ├── ProductPageEntity.swift │ │ │ ├── BrandProductEntity.swift │ │ │ └── EventProductEntity.swift │ │ ├── GiftItemEntity.swift │ │ ├── Curation │ │ │ ├── CurationEntity.swift │ │ │ ├── EventImageEntity.swift │ │ │ ├── EventBannerDetailEntity.swift │ │ │ └── HomeBannerEntity.swift │ │ ├── Filter │ │ │ ├── FilterCellItem.swift │ │ │ ├── FilterType.swift │ │ │ └── FilterEntity.swift │ │ └── EventTag.swift │ ├── Extension │ │ ├── NSObject+.swift │ │ ├── UICollectionReusableView+.swift │ │ ├── UIControl+.swift │ │ ├── UIImage+.swift │ │ ├── UITextField+.swift │ │ ├── CALayer+.swift │ │ ├── UILabel+.swift │ │ ├── DateFormatter+.swift │ │ ├── UITableView+.swift │ │ ├── NSMutableAttributedString+.swift │ │ ├── CGFloat+.swift │ │ ├── CombineCocoa+.swift │ │ ├── URLRequest+.swift │ │ ├── UIImageView+.swift │ │ ├── UICollectionView+.swift │ │ ├── UIView+.swift │ │ └── String+.swift │ ├── Common │ │ ├── Protocol │ │ │ ├── ProductPresentable.swift │ │ │ ├── ViewHolable.swift │ │ │ ├── FavoriteHomeDelegate.swift │ │ │ ├── ItemIdentifier.swift │ │ │ ├── ClassNameRenderable.swift │ │ │ ├── ProductListDelegate.swift │ │ │ ├── CommonProductPageViewControllerRenderable.swift │ │ │ ├── TopCollectionViewDatasource.swift │ │ │ └── FilterRenderable.swift │ │ ├── SearchBar │ │ │ └── SearchBarViewDelegate.swift │ │ ├── NavigationBar │ │ │ ├── BackNavigationViewDelegate.swift │ │ │ └── TitleNavigationViewDelegate.swift │ │ ├── Popup │ │ │ └── PopupViewDelegate.swift │ │ ├── ReusableView │ │ │ ├── SearchFilterHeaderDelegate.swift │ │ │ ├── LoadingReusableView+ViewHolder.swift │ │ │ ├── LoadingReusableView.swift │ │ │ └── ItemHeaderTitleView.swift │ │ ├── UserEvent │ │ │ ├── UserEvent.swift │ │ │ └── FoodChemistryEvent │ │ │ │ └── FoodChemistryCell.swift │ │ ├── Interface │ │ │ └── CommonProduct.swift │ │ ├── Combine │ │ │ ├── ControlPublisher.swift │ │ │ ├── GesturePublisher.swift │ │ │ ├── ControlSubscription.swift │ │ │ └── GestureSubscription.swift │ │ ├── Footer │ │ │ └── LineFooter.swift │ │ ├── Button │ │ │ ├── SortButton.swift │ │ │ └── SelectionButton.swift │ │ ├── Cell │ │ │ └── EmptyCell.swift │ │ └── BottomSheet │ │ │ └── BottomSheetDismissAnimator.swift │ ├── DetailReview │ │ └── Review.swift │ ├── ProductDetail │ │ └── Subview │ │ │ ├── ReviewWriteCell │ │ │ └── ProductDetailReviewWriteCellDelegate.swift │ │ │ └── ImageCell │ │ │ ├── ProductDetailImageCell.swift │ │ │ └── ProductDetailImageCell+ViewHolder.swift │ ├── Application │ │ └── AppComponent.swift │ ├── Networking │ │ ├── NetworkRequestable.swift │ │ ├── Config │ │ │ └── Config.swift │ │ ├── Auth │ │ │ └── AuthAPIService.swift │ │ └── NetworkRequestBuilder.swift │ ├── Pyonsnal-Color.entitlements │ ├── Service │ │ ├── AppInfoService.swift │ │ ├── UserAuthService.swift │ │ ├── KakaoLoginService.swift │ │ └── UserInfoService.swift │ ├── 3rdParty │ │ └── SnapKit+.swift │ ├── ProfileHome │ │ └── SettingInfo.swift │ ├── TermsOfUse │ │ ├── SubTerms.swift │ │ ├── TermsOfUseBuilder.swift │ │ └── View │ │ │ └── TermsButton.swift │ ├── ReviewPopup │ │ ├── ReviewPopupRouter.swift │ │ └── ReviewPopupInteractor.swift │ ├── CommonWeb │ │ └── CommonWebRouter.swift │ ├── LoginPopup │ │ ├── LoginPopupRouter.swift │ │ ├── LoginPopupBuilder.swift │ │ ├── LoginPopupInteractor.swift │ │ └── LoginPopupViewHolder.swift │ ├── ProfileEdit │ │ ├── ProfileEditRouter.swift │ │ └── ProfileEditBuilder.swift │ ├── LogoutPopup │ │ └── LogoutPopupRouter.swift │ ├── ProductFilter │ │ ├── ProductFilterRouter.swift │ │ ├── ProductFilterBuilder.swift │ │ └── ProductFilterInteractor.swift │ ├── NotificationList │ │ ├── NotificationListRouter.swift │ │ ├── NotificationListInteractor.swift │ │ └── NotificationListBuilder.swift │ ├── RootTabBar │ │ ├── RootTabBarComponent.swift │ │ └── RootTabBarInteractor.swift │ ├── LoggedIn │ │ ├── LoggedInInteractor.swift │ │ └── LoggedInBuilder.swift │ ├── ProductSearch │ │ └── SortBottomSheet │ │ │ └── ProductSearchSortBottomSheetRouter.swift │ ├── DesignSystem │ │ └── Spacing.swift │ ├── ProductHome │ │ └── Views │ │ │ ├── ReusableView │ │ │ ├── EventBannerHeaderView.swift │ │ │ └── CurationFooterView.swift │ │ │ └── Cell │ │ │ └── CurationImageCell.swift │ └── Root │ │ ├── RootComponent.swift │ │ └── RootBuilder.swift └── Pyonsnal-Color.xcodeproj │ ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist │ └── xcuserdata │ └── gamza.xcuserdatad │ └── xcschemes │ └── xcschememanagement.plist └── .github └── PULL_REQUEST_TEMPLATE.md /Pyonsnal-Color/.swiftlint.yml: -------------------------------------------------------------------------------- 1 | disabled_rules: 2 | - trailing_whitespace 3 | 4 | line_length: 100 5 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Resource/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Resource/Assets.xcassets/Common/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Resource/Assets.xcassets/Filter/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Resource/Assets.xcassets/Icon/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Resource/Assets.xcassets/Others/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Resource/Assets.xcassets/Profile/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Resource/Assets.xcassets/Review/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Resource/Assets.xcassets/Splash/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Resource/Assets.xcassets/TabBar/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Resource/Assets.xcassets/login/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Resource/Assets.xcassets/HeaderView/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Resource/Assets.xcassets/ServiceTag/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Resource/Assets.xcassets/StoreLogo/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Resource/Assets.xcassets/StoreTag/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ### 이슈 2 | 3 | 4 | ### 수정사항 5 | 6 | 7 | ### (기타, 스크린샷, 트러블슈팅) (선택 사항) 8 | 9 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Resource/Assets.xcassets/LineIconLarge/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Resource/Assets.xcassets/LineIconMedium/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Resource/Assets.xcassets/LineIconSmall/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Resource/Font/Pretendard-Medium.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YAPP-Github/PyonsnalColor-iOS/HEAD/Pyonsnal-Color/Pyonsnal-Color/Resource/Font/Pretendard-Medium.otf -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Resource/Font/Pretendard-Regular.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YAPP-Github/PyonsnalColor-iOS/HEAD/Pyonsnal-Color/Pyonsnal-Color/Resource/Font/Pretendard-Regular.otf -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Resource/Font/Pretendard-SemiBold.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YAPP-Github/PyonsnalColor-iOS/HEAD/Pyonsnal-Color/Pyonsnal-Color/Resource/Font/Pretendard-SemiBold.otf -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Resource/Assets.xcassets/Icon/logo.imageset/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YAPP-Github/PyonsnalColor-iOS/HEAD/Pyonsnal-Color/Pyonsnal-Color/Resource/Assets.xcassets/Icon/logo.imageset/logo.png -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Resource/Assets.xcassets/AppIcon.appiconset/AppIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YAPP-Github/PyonsnalColor-iOS/HEAD/Pyonsnal-Color/Pyonsnal-Color/Resource/Assets.xcassets/AppIcon.appiconset/AppIcon.png -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Resource/Assets.xcassets/Icon/logo.imageset/logo@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YAPP-Github/PyonsnalColor-iOS/HEAD/Pyonsnal-Color/Pyonsnal-Color/Resource/Assets.xcassets/Icon/logo.imageset/logo@2x.png -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Resource/Assets.xcassets/Icon/logo.imageset/logo@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YAPP-Github/PyonsnalColor-iOS/HEAD/Pyonsnal-Color/Pyonsnal-Color/Resource/Assets.xcassets/Icon/logo.imageset/logo@3x.png -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Entity/Common/EmptyResponse.swift: -------------------------------------------------------------------------------- 1 | // 2 | // EmptyResponse.swift 3 | // Pyonsnal-Color 4 | // 5 | // Created by 조소정 on 2023/07/08. 6 | // 7 | 8 | import Foundation 9 | 10 | struct EmptyResponse: Codable {} 11 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Extension/NSObject+.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NSObject+.swift 3 | // Pyonsnal-Color 4 | // 5 | // Created by 조소정 on 2023/06/23. 6 | // 7 | 8 | import Foundation 9 | 10 | extension NSObject: ClassNameRenderable {} 11 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Resource/Assets.xcassets/Icon/icon_header.imageset/Frame 8907.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YAPP-Github/PyonsnalColor-iOS/HEAD/Pyonsnal-Color/Pyonsnal-Color/Resource/Assets.xcassets/Icon/icon_header.imageset/Frame 8907.png -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Resource/Assets.xcassets/Icon/icon_search.imageset/icon_search.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YAPP-Github/PyonsnalColor-iOS/HEAD/Pyonsnal-Color/Pyonsnal-Color/Resource/Assets.xcassets/Icon/icon_search.imageset/icon_search.png -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Resource/Assets.xcassets/Profile/profile_edit.imageset/edit_24.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YAPP-Github/PyonsnalColor-iOS/HEAD/Pyonsnal-Color/Pyonsnal-Color/Resource/Assets.xcassets/Profile/profile_edit.imageset/edit_24.png -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Entity/Review/ReviewFeedKind.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ReviewFeedKind.swift 3 | // Pyonsnal-Color 4 | // 5 | // Created by 김진우 on 2023/10/03. 6 | // 7 | 8 | enum ReviewFeedKind { 9 | case good 10 | case bad 11 | } 12 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Resource/Assets.xcassets/Icon/icon_header.imageset/Frame 8907@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YAPP-Github/PyonsnalColor-iOS/HEAD/Pyonsnal-Color/Pyonsnal-Color/Resource/Assets.xcassets/Icon/icon_header.imageset/Frame 8907@2x.png -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Resource/Assets.xcassets/Icon/icon_header.imageset/Frame 8907@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YAPP-Github/PyonsnalColor-iOS/HEAD/Pyonsnal-Color/Pyonsnal-Color/Resource/Assets.xcassets/Icon/icon_header.imageset/Frame 8907@3x.png -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Resource/Assets.xcassets/Icon/icon_search.imageset/icon_search@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YAPP-Github/PyonsnalColor-iOS/HEAD/Pyonsnal-Color/Pyonsnal-Color/Resource/Assets.xcassets/Icon/icon_search.imageset/icon_search@2x.png -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Resource/Assets.xcassets/Icon/icon_search.imageset/icon_search@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YAPP-Github/PyonsnalColor-iOS/HEAD/Pyonsnal-Color/Pyonsnal-Color/Resource/Assets.xcassets/Icon/icon_search.imageset/icon_search@3x.png -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Resource/Assets.xcassets/Profile/profile_edit.imageset/edit_24@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YAPP-Github/PyonsnalColor-iOS/HEAD/Pyonsnal-Color/Pyonsnal-Color/Resource/Assets.xcassets/Profile/profile_edit.imageset/edit_24@2x.png -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Resource/Assets.xcassets/Profile/profile_edit.imageset/edit_24@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YAPP-Github/PyonsnalColor-iOS/HEAD/Pyonsnal-Color/Pyonsnal-Color/Resource/Assets.xcassets/Profile/profile_edit.imageset/edit_24@3x.png -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Resource/Assets.xcassets/ServiceTag/tag_store.imageset/tag_store.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YAPP-Github/PyonsnalColor-iOS/HEAD/Pyonsnal-Color/Pyonsnal-Color/Resource/Assets.xcassets/ServiceTag/tag_store.imageset/tag_store.png -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Resource/Assets.xcassets/AccentColor.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "idiom" : "universal" 5 | } 6 | ], 7 | "info" : { 8 | "author" : "xcode", 9 | "version" : 1 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Resource/Assets.xcassets/Splash/image_splash.imageset/image_splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YAPP-Github/PyonsnalColor-iOS/HEAD/Pyonsnal-Color/Pyonsnal-Color/Resource/Assets.xcassets/Splash/image_splash.imageset/image_splash.png -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Common/Protocol/ProductPresentable.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ProductPresentable.swift 3 | // Pyonsnal-Color 4 | // 5 | // Created by 김인호 on 2023/07/15. 6 | // 7 | 8 | protocol ProductPresentable { 9 | func didTabRootTabBar() 10 | } 11 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Resource/Assets.xcassets/Common/check.imageset/check.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Resource/Assets.xcassets/Profile/profile_plus.imageset/profile_plus@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YAPP-Github/PyonsnalColor-iOS/HEAD/Pyonsnal-Color/Pyonsnal-Color/Resource/Assets.xcassets/Profile/profile_plus.imageset/profile_plus@3x.png -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Resource/Assets.xcassets/Splash/image_splash.imageset/image_splash@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YAPP-Github/PyonsnalColor-iOS/HEAD/Pyonsnal-Color/Pyonsnal-Color/Resource/Assets.xcassets/Splash/image_splash.imageset/image_splash@2x.png -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Resource/Assets.xcassets/Splash/image_splash.imageset/image_splash@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YAPP-Github/PyonsnalColor-iOS/HEAD/Pyonsnal-Color/Pyonsnal-Color/Resource/Assets.xcassets/Splash/image_splash.imageset/image_splash@3x.png -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Resource/Assets.xcassets/StoreTag/tag_store_CU.imageset/tag_store_CU.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YAPP-Github/PyonsnalColor-iOS/HEAD/Pyonsnal-Color/Pyonsnal-Color/Resource/Assets.xcassets/StoreTag/tag_store_CU.imageset/tag_store_CU.png -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Resource/Assets.xcassets/StoreTag/tag_store_GS.imageset/tag_store_GS.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YAPP-Github/PyonsnalColor-iOS/HEAD/Pyonsnal-Color/Pyonsnal-Color/Resource/Assets.xcassets/StoreTag/tag_store_GS.imageset/tag_store_GS.png -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Resource/Assets.xcassets/StoreLogo/store_logo_CU.imageset/store logo_CU.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YAPP-Github/PyonsnalColor-iOS/HEAD/Pyonsnal-Color/Pyonsnal-Color/Resource/Assets.xcassets/StoreLogo/store_logo_CU.imageset/store logo_CU.png -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Resource/Assets.xcassets/StoreLogo/store_logo_GS.imageset/store logo_GS.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YAPP-Github/PyonsnalColor-iOS/HEAD/Pyonsnal-Color/Pyonsnal-Color/Resource/Assets.xcassets/StoreLogo/store_logo_GS.imageset/store logo_GS.png -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Entity/Auth/AuthType.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AuthType.swift 3 | // Pyonsnal-Color 4 | // 5 | // Created by 조소정 on 2023/07/15. 6 | // 7 | 8 | import Foundation 9 | 10 | enum AuthType: String { 11 | case kakao 12 | case apple 13 | } 14 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Resource/Assets.xcassets/Icon/icon_pyonsnalColor.imageset/icon_pyonsnalColor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YAPP-Github/PyonsnalColor-iOS/HEAD/Pyonsnal-Color/Pyonsnal-Color/Resource/Assets.xcassets/Icon/icon_pyonsnalColor.imageset/icon_pyonsnalColor.png -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Entity/Member/NicknameEntity.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NicknameEntity.swift 3 | // Pyonsnal-Color 4 | // 5 | // Created by 조소정 on 11/20/23. 6 | // 7 | 8 | import Foundation 9 | 10 | struct NicknameEntity: Codable { 11 | var nickname: String 12 | } 13 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Extension/UICollectionReusableView+.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UICollectionReusableView+.swift 3 | // Pyonsnal-Color 4 | // 5 | // Created by 김진우 on 2023/10/15. 6 | // 7 | 8 | import UIKit 9 | 10 | extension UICollectionReusableView: ItemIdentifier {} 11 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Resource/Assets.xcassets/Icon/icon_pyonsnalColor.imageset/icon_pyonsnalColor@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YAPP-Github/PyonsnalColor-iOS/HEAD/Pyonsnal-Color/Pyonsnal-Color/Resource/Assets.xcassets/Icon/icon_pyonsnalColor.imageset/icon_pyonsnalColor@2x.png -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Resource/Assets.xcassets/Icon/icon_pyonsnalColor.imageset/icon_pyonsnalColor@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YAPP-Github/PyonsnalColor-iOS/HEAD/Pyonsnal-Color/Pyonsnal-Color/Resource/Assets.xcassets/Icon/icon_pyonsnalColor.imageset/icon_pyonsnalColor@3x.png -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Resource/Assets.xcassets/StoreTag/tag_store_7eleven.imageset/tag_store_7eleven.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YAPP-Github/PyonsnalColor-iOS/HEAD/Pyonsnal-Color/Pyonsnal-Color/Resource/Assets.xcassets/StoreTag/tag_store_7eleven.imageset/tag_store_7eleven.png -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Resource/Assets.xcassets/StoreTag/tag_store_emart24.imageset/tag_store_emart24.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YAPP-Github/PyonsnalColor-iOS/HEAD/Pyonsnal-Color/Pyonsnal-Color/Resource/Assets.xcassets/StoreTag/tag_store_emart24.imageset/tag_store_emart24.png -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Resource/Assets.xcassets/StoreLogo/store_logo_emart24.imageset/store logo_emart24.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YAPP-Github/PyonsnalColor-iOS/HEAD/Pyonsnal-Color/Pyonsnal-Color/Resource/Assets.xcassets/StoreLogo/store_logo_emart24.imageset/store logo_emart24.png -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Common/SearchBar/SearchBarViewDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SearchBarViewDelegate.swift 3 | // Pyonsnal-Color 4 | // 5 | // Created by 김진우 on 2023/07/21. 6 | // 7 | 8 | protocol SearchBarViewDelegate: AnyObject { 9 | func updateText(_ text: String?) 10 | } 11 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Resource/Assets.xcassets/Common/icon_close.imageset/icon_close.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Resource/Assets.xcassets/Common/check.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "check.svg", 5 | "idiom" : "universal" 6 | } 7 | ], 8 | "info" : { 9 | "author" : "xcode", 10 | "version" : 1 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Resource/Assets.xcassets/StoreLogo/store_logo_7-eleven.imageset/store logo_GS_7-eleven.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YAPP-Github/PyonsnalColor-iOS/HEAD/Pyonsnal-Color/Pyonsnal-Color/Resource/Assets.xcassets/StoreLogo/store_logo_7-eleven.imageset/store logo_GS_7-eleven.png -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Common/NavigationBar/BackNavigationViewDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BackNavigationViewDelegate.swift 3 | // Pyonsnal-Color 4 | // 5 | // Created by 김진우 on 2023/07/01. 6 | // 7 | 8 | protocol BackNavigationViewDelegate: AnyObject { 9 | func didTapBackButton() 10 | } 11 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/DetailReview/Review.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Review.swift 3 | // Pyonsnal-Color 4 | // 5 | // Created by 김인호 on 2023/09/22. 6 | // 7 | 8 | import Foundation 9 | 10 | struct Review { 11 | let kind: ReviewEvaluationKind 12 | let state: ReviewEvaluationState 13 | } 14 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Entity/Auth/UserLoginStatusEntity.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UserLoginStatusEntity.swift 3 | // Pyonsnal-Color 4 | // 5 | // Created by 조소정 on 2023/07/15. 6 | // 7 | 8 | import Foundation 9 | 10 | struct UserLoginStatusEntity: Decodable { 11 | var isJoined: Bool 12 | } 13 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Resource/Assets.xcassets/Common/icon_title.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "Layer_1.svg", 5 | "idiom" : "universal" 6 | } 7 | ], 8 | "info" : { 9 | "author" : "xcode", 10 | "version" : 1 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Resource/Assets.xcassets/LineIconMedium/icon_close.imageset/icon_close.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Entity/Common/ProductType.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ProductType.swift 3 | // Pyonsnal-Color 4 | // 5 | // Created by 조소정 on 2023/09/16. 6 | // 7 | 8 | import Foundation 9 | 10 | enum ProductType: String, Decodable { 11 | case pb = "PB" 12 | case event = "EVENT" 13 | } 14 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Resource/Assets.xcassets/Common/icon_arrow.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "icon_detail.svg", 5 | "idiom" : "universal" 6 | } 7 | ], 8 | "info" : { 9 | "author" : "xcode", 10 | "version" : 1 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Resource/Assets.xcassets/Common/icon_close.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "icon_close.svg", 5 | "idiom" : "universal" 6 | } 7 | ], 8 | "info" : { 9 | "author" : "xcode", 10 | "version" : 1 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Resource/Assets.xcassets/Filter/icon_filter.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "icon_filter.svg", 5 | "idiom" : "universal" 6 | } 7 | ], 8 | "info" : { 9 | "author" : "xcode", 10 | "version" : 1 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Resource/Assets.xcassets/HeaderView/icon_back.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "icon_back.svg", 5 | "idiom" : "universal" 6 | } 7 | ], 8 | "info" : { 9 | "author" : "xcode", 10 | "version" : 1 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Resource/Assets.xcassets/Icon/icon_check.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "icon_check.svg", 5 | "idiom" : "universal" 6 | } 7 | ], 8 | "info" : { 9 | "author" : "xcode", 10 | "version" : 1 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Resource/Assets.xcassets/Icon/icon_delete.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "icon_delete.svg", 5 | "idiom" : "universal" 6 | } 7 | ], 8 | "info" : { 9 | "author" : "xcode", 10 | "version" : 1 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Resource/Assets.xcassets/Others/icon_delete.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "icon_delete.svg", 5 | "idiom" : "universal" 6 | } 7 | ], 8 | "info" : { 9 | "author" : "xcode", 10 | "version" : 1 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Resource/Assets.xcassets/Others/icon_revert.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "icon_revert.svg", 5 | "idiom" : "universal" 6 | } 7 | ], 8 | "info" : { 9 | "author" : "xcode", 10 | "version" : 1 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Resource/Assets.xcassets/Review/icon_review.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "icon_review.svg", 5 | "idiom" : "universal" 6 | } 7 | ], 8 | "info" : { 9 | "author" : "xcode", 10 | "version" : 1 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Resource/Assets.xcassets/ServiceTag/tag_store.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "tag_store.png", 5 | "idiom" : "universal" 6 | } 7 | ], 8 | "info" : { 9 | "author" : "xcode", 10 | "version" : 1 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Resource/Assets.xcassets/login/login_apple.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "login-apple.svg", 5 | "idiom" : "universal" 6 | } 7 | ], 8 | "info" : { 9 | "author" : "xcode", 10 | "version" : 1 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Resource/Assets.xcassets/login/login_kakao.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "login_kakao.svg", 5 | "idiom" : "universal" 6 | } 7 | ], 8 | "info" : { 9 | "author" : "xcode", 10 | "version" : 1 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Resource/Assets.xcassets/Common/icon_arrow.imageset/icon_detail.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Resource/Assets.xcassets/HeaderView/bellSimple.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "BellSimple.svg", 5 | "idiom" : "universal" 6 | } 7 | ], 8 | "info" : { 9 | "author" : "xcode", 10 | "version" : 1 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Resource/Assets.xcassets/LineIconLarge/icon_share.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "icon_share.svg", 5 | "idiom" : "universal" 6 | } 7 | ], 8 | "info" : { 9 | "author" : "xcode", 10 | "version" : 1 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Resource/Assets.xcassets/LineIconMedium/icon_back.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "icon_back.svg", 5 | "idiom" : "universal" 6 | } 7 | ], 8 | "info" : { 9 | "author" : "xcode", 10 | "version" : 1 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Resource/Assets.xcassets/LineIconMedium/icon_next.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "icon_next.svg", 5 | "idiom" : "universal" 6 | } 7 | ], 8 | "info" : { 9 | "author" : "xcode", 10 | "version" : 1 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Resource/Assets.xcassets/LineIconSmall/icon_close_small.imageset/icon_close_small.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Resource/Assets.xcassets/StoreTag/tag_store_CU.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "tag_store_CU.png", 5 | "idiom" : "universal" 6 | } 7 | ], 8 | "info" : { 9 | "author" : "xcode", 10 | "version" : 1 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Resource/Assets.xcassets/StoreTag/tag_store_GS.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "tag_store_GS.png", 5 | "idiom" : "universal" 6 | } 7 | ], 8 | "info" : { 9 | "author" : "xcode", 10 | "version" : 1 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Resource/Assets.xcassets/Common/check.selected.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "check.selected.svg", 5 | "idiom" : "universal" 6 | } 7 | ], 8 | "info" : { 9 | "author" : "xcode", 10 | "version" : 1 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Resource/Assets.xcassets/Filter/icon_filter_refresh.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "icon_revert.svg", 5 | "idiom" : "universal" 6 | } 7 | ], 8 | "info" : { 9 | "author" : "xcode", 10 | "version" : 1 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Resource/Assets.xcassets/Filter/icon_filter_view.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "icon_filter_view.svg", 5 | "idiom" : "universal" 6 | } 7 | ], 8 | "info" : { 9 | "author" : "xcode", 10 | "version" : 1 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Resource/Assets.xcassets/Icon/icon_delete_medium.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "icon_delete(m).svg", 5 | "idiom" : "universal" 6 | } 7 | ], 8 | "info" : { 9 | "author" : "xcode", 10 | "version" : 1 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Resource/Assets.xcassets/LineIconMedium/icon_alarm.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "icon_alarm.svg", 5 | "idiom" : "universal" 6 | } 7 | ], 8 | "info" : { 9 | "author" : "xcode", 10 | "version" : 1 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Resource/Assets.xcassets/LineIconMedium/icon_check.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "icon_check.svg", 5 | "idiom" : "universal" 6 | } 7 | ], 8 | "info" : { 9 | "author" : "xcode", 10 | "version" : 1 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Resource/Assets.xcassets/LineIconMedium/icon_close.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "icon_close.svg", 5 | "idiom" : "universal" 6 | } 7 | ], 8 | "info" : { 9 | "author" : "xcode", 10 | "version" : 1 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Resource/Assets.xcassets/LineIconSmall/icon_detail.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "icon_detail.svg", 5 | "idiom" : "universal" 6 | } 7 | ], 8 | "info" : { 9 | "author" : "xcode", 10 | "version" : 1 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Resource/Assets.xcassets/LineIconSmall/icon_filter.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "icon_filter.svg", 5 | "idiom" : "universal" 6 | } 7 | ], 8 | "info" : { 9 | "author" : "xcode", 10 | "version" : 1 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Resource/Assets.xcassets/StoreLogo/store_logo_CU.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "store logo_CU.png", 5 | "idiom" : "universal" 6 | } 7 | ], 8 | "info" : { 9 | "author" : "xcode", 10 | "version" : 1 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Resource/Assets.xcassets/StoreLogo/store_logo_GS.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "store logo_GS.png", 5 | "idiom" : "universal" 6 | } 7 | ], 8 | "info" : { 9 | "author" : "xcode", 10 | "version" : 1 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Resource/Assets.xcassets/Common/check.selected.red.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "icon_check_selected.svg", 5 | "idiom" : "universal" 6 | } 7 | ], 8 | "info" : { 9 | "author" : "xcode", 10 | "version" : 1 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Resource/Assets.xcassets/LineIconMedium/icon_next.imageset/icon_next.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Resource/Assets.xcassets/LineIconSmall/icon_detail.imageset/icon_detail.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Resource/Assets.xcassets/LineIconSmall/icon_filter.imageset/icon_filter.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Resource/Assets.xcassets/Review/star_large.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "filled=unfilled, size=L.svg", 5 | "idiom" : "universal" 6 | } 7 | ], 8 | "info" : { 9 | "author" : "xcode", 10 | "version" : 1 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Resource/Assets.xcassets/Review/star_medium.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "filled=unfilled, size=m.svg", 5 | "idiom" : "universal" 6 | } 7 | ], 8 | "info" : { 9 | "author" : "xcode", 10 | "version" : 1 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Resource/Assets.xcassets/StoreTag/tag_store_7eleven.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "tag_store_7eleven.png", 5 | "idiom" : "universal" 6 | } 7 | ], 8 | "info" : { 9 | "author" : "xcode", 10 | "version" : 1 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Resource/Assets.xcassets/StoreTag/tag_store_emart24.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "tag_store_emart24.png", 5 | "idiom" : "universal" 6 | } 7 | ], 8 | "info" : { 9 | "author" : "xcode", 10 | "version" : 1 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Common/Protocol/ViewHolable.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewHolable.swift 3 | // Pyonsnal-Color 4 | // 5 | // Created by 김진우 on 2023/06/10. 6 | // 7 | 8 | import UIKit 9 | 10 | protocol ViewHolderable { 11 | func place(in view: UIView) 12 | func configureConstraints(for view: UIView) 13 | } 14 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Entity/Products/ProductDetailSection.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ProductDetailSection.swift 3 | // Pyonsnal-Color 4 | // 5 | // Created by 김진우 on 2023/09/17. 6 | // 7 | 8 | enum ProductDetailSection { 9 | case image 10 | case information 11 | case reviewWrite 12 | case review 13 | } 14 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Resource/Assets.xcassets/Filter/icon_filter.imageset/icon_filter.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Resource/Assets.xcassets/LineIconMedium/icon_back.imageset/icon_back.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Resource/Assets.xcassets/LineIconSmall/icon_close_small.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "icon_close_small.svg", 5 | "idiom" : "universal" 6 | } 7 | ], 8 | "info" : { 9 | "author" : "xcode", 10 | "version" : 1 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Resource/Assets.xcassets/LineIconSmall/icon_filter_view.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "icon_filter_view.svg", 5 | "idiom" : "universal" 6 | } 7 | ], 8 | "info" : { 9 | "author" : "xcode", 10 | "version" : 1 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Resource/Assets.xcassets/Review/star_filled_large.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "filled=filled, size=L.svg", 5 | "idiom" : "universal" 6 | } 7 | ], 8 | "info" : { 9 | "author" : "xcode", 10 | "version" : 1 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Resource/Assets.xcassets/Review/star_filled_medium.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "filled=filled, size=m.svg", 5 | "idiom" : "universal" 6 | } 7 | ], 8 | "info" : { 9 | "author" : "xcode", 10 | "version" : 1 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Resource/Assets.xcassets/StoreLogo/store_logo_emart24.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "store logo_emart24.png", 5 | "idiom" : "universal" 6 | } 7 | ], 8 | "info" : { 9 | "author" : "xcode", 10 | "version" : 1 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Resource/Assets.xcassets/TabBar/icon_tab_my_selected.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "icon_tab_my_selected.svg", 5 | "idiom" : "universal" 6 | } 7 | ], 8 | "info" : { 9 | "author" : "xcode", 10 | "version" : 1 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Common/Popup/PopupViewDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PopupViewDelegate.swift 3 | // Pyonsnal-Color 4 | // 5 | // Created by 김인호 on 1/31/24. 6 | // 7 | 8 | import Foundation 9 | 10 | protocol PopupViewDelegate: AnyObject { 11 | func didTapConfirmButton() 12 | func didTapDismissButton() 13 | } 14 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Common/ReusableView/SearchFilterHeaderDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SearchFilterHeaderDelegate.swift 3 | // Pyonsnal-Color 4 | // 5 | // Created by 김진우 on 2023/07/29. 6 | // 7 | 8 | protocol SearchFilterHeaderDelegate: AnyObject { 9 | func didTapSortButton(filterItem: FilterItemEntity) 10 | } 11 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Resource/Assets.xcassets/Icon/icon_check.imageset/icon_check.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Resource/Assets.xcassets/StoreLogo/store_logo_7-eleven.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "store logo_GS_7-eleven.png", 5 | "idiom" : "universal" 6 | } 7 | ], 8 | "info" : { 9 | "author" : "xcode", 10 | "version" : 1 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Resource/Assets.xcassets/TabBar/icon_tab_home_selected.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "icon_tab_home_selected.svg", 5 | "idiom" : "universal" 6 | } 7 | ], 8 | "info" : { 9 | "author" : "xcode", 10 | "version" : 1 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Resource/Assets.xcassets/TabBar/icon_tab_my_unselected.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "icon_tab_my_unselected.svg", 5 | "idiom" : "universal" 6 | } 7 | ], 8 | "info" : { 9 | "author" : "xcode", 10 | "version" : 1 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Resource/Assets.xcassets/TabBar/icon_tab_sale_selected.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "icon_tab_sale_selected.svg", 5 | "idiom" : "universal" 6 | } 7 | ], 8 | "info" : { 9 | "author" : "xcode", 10 | "version" : 1 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Entity/GiftItemEntity.swift: -------------------------------------------------------------------------------- 1 | // 2 | // GiftItemEntity.swift 3 | // Pyonsnal-Color 4 | // 5 | // Created by 김진우 on 2023/06/24. 6 | // 7 | 8 | import Foundation 9 | 10 | struct GiftItemEntity: Decodable, Hashable { 11 | let name: String 12 | let price: String 13 | let imageURL: URL 14 | } 15 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Entity/Products/ProductGiftEntity.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ProductGiftEntity.swift 3 | // Pyonsnal-Color 4 | // 5 | // Created by 김진우 on 2023/10/09. 6 | // 7 | 8 | import Foundation 9 | 10 | struct ProductGiftEntity { 11 | let imageURL: URL 12 | let name: String 13 | let price: String 14 | } 15 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Resource/Assets.xcassets/TabBar/icon_tab_home_unselected.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "icon_tab_home_unselected.svg", 5 | "idiom" : "universal" 6 | } 7 | ], 8 | "info" : { 9 | "author" : "xcode", 10 | "version" : 1 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Resource/Assets.xcassets/TabBar/icon_tab_sale_unselected.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "icon_tab_sale_unselected.svg", 5 | "idiom" : "universal" 6 | } 7 | ], 8 | "info" : { 9 | "author" : "xcode", 10 | "version" : 1 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Entity/Common/ErrorResponse.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ErrorResponse.swift 3 | // Pyonsnal-Color 4 | // 5 | // Created by 조소정 on 2023/07/03. 6 | // 7 | 8 | import Foundation 9 | 10 | struct ErrorResponse: Decodable { 11 | let code: String? 12 | let message: String? 13 | let bodyString: String? 14 | } 15 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Entity/Products/ProductDetailSectionModel.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ProductDetailSectionModel.swift 3 | // Pyonsnal-Color 4 | // 5 | // Created by 김진우 on 2023/09/18. 6 | // 7 | 8 | struct ProductDetailSectionModel { 9 | let section: ProductDetailSection 10 | let items: [ProductDetailSectionItem] 11 | } 12 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Resource/Assets.xcassets/LineIconMedium/icon_check.imageset/icon_check.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Entity/Auth/UserAuthEntity.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UserAuthEntity.swift 3 | // Pyonsnal-Color 4 | // 5 | // Created by 김진우 on 2023/07/08. 6 | // 7 | 8 | struct UserAuthEntity: Codable { 9 | let isFirstLogin: Bool 10 | let isGuest: Bool 11 | let accessToken: String 12 | let refreshToken: String 13 | } 14 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Resource/Constants/Url/ProfileUrl.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ProfileUrl.swift 3 | // Pyonsnal-Color 4 | // 5 | // Created by 조소정 on 2023/07/07. 6 | // 7 | 8 | import Foundation 9 | 10 | struct ProfileUrl { 11 | static let team = "https://pentagonal-roquefort-ccf.notion.site/ae5a240aecf540f3bf8f0364aa15f7b7?pvs=4" 12 | } 13 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Common/NavigationBar/TitleNavigationViewDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // File.swift 3 | // Pyonsnal-Color 4 | // 5 | // Created by 김인호 on 2023/07/04. 6 | // 7 | 8 | import Foundation 9 | 10 | protocol TitleNavigationViewDelegate: AnyObject { 11 | func didTabSearchButton() 12 | func didTabNotificationButton() 13 | } 14 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Resource/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "AppIcon.png", 5 | "idiom" : "universal", 6 | "platform" : "ios", 7 | "size" : "1024x1024" 8 | } 9 | ], 10 | "info" : { 11 | "author" : "xcode", 12 | "version" : 1 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Resource/Assets.xcassets/HeaderView/icon_back.imageset/icon_back.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Common/Protocol/FavoriteHomeDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FavoriteHomeDelegate.swift 3 | // Pyonsnal-Color 4 | // 5 | // Created by 조소정 on 11/11/23. 6 | // 7 | 8 | import Foundation 9 | 10 | protocol FavoriteHomeDelegate: AnyObject { 11 | func didTapFavoriteButton(product: ProductDetailEntity, action: FavoriteButtonAction) 12 | } 13 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Entity/Review/UserReviewInput.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UserReviewInput.swift 3 | // Pyonsnal-Color 4 | // 5 | // Created by 김인호 on 12/28/23. 6 | // 7 | 8 | import UIKit 9 | 10 | struct UserReviewInput { 11 | let reviews: [ReviewEvaluationKind : ReviewEvaluationState] 12 | let reviewContents: String 13 | let reviewImage: UIImage? 14 | } 15 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Entity/Review/ReviewEvaluationState.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ReviewEvaluationState.swift 3 | // Pyonsnal-Color 4 | // 5 | // Created by 김진우 on 2023/09/18. 6 | // 7 | 8 | enum ReviewEvaluationState: String { 9 | case good = "GOOD" 10 | case normal = "NORMAL" 11 | case bad = "BAD" 12 | } 13 | 14 | extension ReviewEvaluationState: Codable { 15 | } 16 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/ProductDetail/Subview/ReviewWriteCell/ProductDetailReviewWriteCellDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ProductDetailReviewWriteCellDelegate.swift 3 | // Pyonsnal-Color 4 | // 5 | // Created by 김진우 on 2023/10/09. 6 | // 7 | 8 | protocol ProductDetailReviewWriteCellDelegate: AnyObject { 9 | func writeButtonDidTap() 10 | func sortButtonDidTap() 11 | } 12 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Entity/Review/ReviewHateCountEntity.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ReviewHateCountEntity.swift 3 | // Pyonsnal-Color 4 | // 5 | // Created by 김진우 on 2023/10/25. 6 | // 7 | 8 | struct ReviewHateCountEntity { 9 | let writerIds: [Int] 10 | let hateCount: Int 11 | } 12 | 13 | extension ReviewHateCountEntity: Hashable {} 14 | extension ReviewHateCountEntity: Codable {} 15 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Entity/Review/ReviewLikeCountEntity.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ReviewLikeCountEntity.swift 3 | // Pyonsnal-Color 4 | // 5 | // Created by 김진우 on 2023/10/25. 6 | // 7 | 8 | struct ReviewLikeCountEntity { 9 | let writerIds: [Int] 10 | let likeCount: Int 11 | } 12 | 13 | extension ReviewLikeCountEntity: Hashable {} 14 | extension ReviewLikeCountEntity: Codable {} 15 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Common/UserEvent/UserEvent.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UserEvent.swift 3 | // Pyonsnal-Color 4 | // 5 | // Created by 김인호 on 1/23/24. 6 | // 7 | 8 | import UIKit 9 | 10 | enum UserEvent: String, Decodable { 11 | case launchingEvent 12 | case foodChemistryEvent 13 | } 14 | 15 | struct UserEventDetail { 16 | let title: String 17 | let urlString: String 18 | } 19 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Resource/Assets.xcassets/Common/check.selected.imageset/check.selected.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Entity/Review/ReviewEvaluationProtocol.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ReviewEvaluationProtocol.swift 3 | // Pyonsnal-Color 4 | // 5 | // Created by 김진우 on 2023/09/30. 6 | // 7 | 8 | protocol ReviewEvaluationProtocol { 9 | var name: String { get } 10 | var goodStateText: String { get } 11 | var normalStateText: String { get } 12 | var badStateText: String { get } 13 | } 14 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Extension/UIControl+.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UIControl+.swift 3 | // Pyonsnal-Color 4 | // 5 | // Created by 조소정 on 2023/09/06. 6 | // 7 | 8 | import UIKit 9 | import Combine 10 | 11 | extension UIControl { 12 | func controlPublisher(for event: UIControl.Event) -> ControlPublisher { 13 | return ControlPublisher(control: self, controlEvent: event) 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Resource/Assets.xcassets/Common/check.selected.red.imageset/icon_check_selected.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Entity/Review/ReviewTasteEvaluation.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ReviewTasteEvaluation.swift 3 | // Pyonsnal-Color 4 | // 5 | // Created by 김진우 on 2023/09/29. 6 | // 7 | 8 | struct ReviewTasteEvaluation: ReviewEvaluationProtocol { 9 | let name: String = "맛" 10 | let goodStateText: String = "맛있어요" 11 | let normalStateText: String = "맛있어요" 12 | let badStateText: String = "맛있어요" 13 | } 14 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Entity/Review/ReviewQualityEvaluation.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ReviewQualityEvaluation.swift 3 | // Pyonsnal-Color 4 | // 5 | // Created by 김진우 on 2023/09/29. 6 | // 7 | 8 | struct ReviewQualityEvaluation: ReviewEvaluationProtocol { 9 | let name: String = "맛" 10 | let goodStateText: String = "좋아요" 11 | let normalStateText: String = "보통이에요" 12 | let badStateText: String = "별로에요" 13 | } 14 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Resource/Assets.xcassets/Icon/icon_delete.imageset/icon_delete.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Common/Protocol/ItemIdentifier.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ItemIdentifier.swift 3 | // Pyonsnal-Color 4 | // 5 | // Created by 조소정 on 2023/06/28. 6 | // 7 | 8 | import Foundation 9 | 10 | protocol ItemIdentifier { 11 | static var identifier: String { get } 12 | } 13 | 14 | extension ItemIdentifier { 15 | static var identifier: String { 16 | return String(describing: self) 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Resource/Assets.xcassets/Others/icon_delete.imageset/icon_delete.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Extension/UIImage+.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UIImage+.swift 3 | // Pyonsnal-Color 4 | // 5 | // Created by 김인호 on 2023/09/22. 6 | // 7 | 8 | import UIKit 9 | 10 | extension UIImage { 11 | func resize(targetSize: CGSize) -> UIImage? { 12 | return UIGraphicsImageRenderer(size: targetSize).image { _ in 13 | draw(in: .init(origin: .zero, size: targetSize)) 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Application/AppComponent.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppComponent.swift 3 | // Pyonsnal-Color 4 | // 5 | // Created by 김진우 on 2023/05/28. 6 | // 7 | 8 | import ModernRIBs 9 | 10 | final class AppComponent: Component, RootDependency { 11 | let userAuthService: UserAuthService = .init(keyChainService: .shared) 12 | init() { 13 | super.init(dependency: EmptyComponent()) 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Entity/Review/ReviewValueForMoneyEvaluation.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ReviewValueForMoneyEvaluation.swift 3 | // Pyonsnal-Color 4 | // 5 | // Created by 김진우 on 2023/09/29. 6 | // 7 | 8 | struct ReviewValueForMoneyEvaluation: ReviewEvaluationProtocol { 9 | let name: String = "맛" 10 | let goodStateText: String = "맛있어요" 11 | let normalStateText: String = "맛있어요" 12 | let badStateText: String = "맛있어요" 13 | } 14 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Common/Protocol/ClassNameRenderable.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ClassNameRenderable.swift 3 | // Pyonsnal-Color 4 | // 5 | // Created by 조소정 on 2023/06/26. 6 | // 7 | 8 | import Foundation 9 | 10 | protocol ClassNameRenderable { 11 | static var className: String { get } 12 | } 13 | 14 | extension ClassNameRenderable { 15 | static var className: String { 16 | return String(describing: self) 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Entity/Member/MemberInfoEntity.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MemberInfoEntity.swift 3 | // Pyonsnal-Color 4 | // 5 | // Created by 조소정 on 2023/07/08. 6 | // 7 | 8 | import Foundation 9 | 10 | struct MemberInfoEntity: Decodable { 11 | var oauthId: String 12 | var oauthType: String 13 | var isGuest: Bool 14 | var profileImage: String? 15 | var memberId: Int 16 | var nickname: String 17 | var email: String 18 | } 19 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Entity/Review/ReviewUploadEntity.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ReviewUploadEntity.swift 3 | // Pyonsnal-Color 4 | // 5 | // Created by 김인호 on 2023/10/09. 6 | // 7 | 8 | import Foundation 9 | 10 | struct ReviewUploadEntity: Encodable { 11 | let taste: String 12 | let quality: String 13 | let valueForMoney: String 14 | let score: Double 15 | let contents: String 16 | let writerId: Int 17 | let writerName: String 18 | } 19 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Resource/ImageAssetKind/ImageAssetKind+Splash.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ImageAssetKind+Splash.swift 3 | // Pyonsnal-Color 4 | // 5 | // Created by 김진우 on 2023/07/08. 6 | // 7 | 8 | import UIKit 9 | 10 | extension ImageAssetKind { 11 | enum Splash: String { 12 | case imageSplash = "image_splash" 13 | 14 | var image: UIImage? { 15 | return .init(named: self.rawValue) 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Entity/Curation/CurationEntity.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CurationEntity.swift 3 | // Pyonsnal-Color 4 | // 5 | // Created by 김인호 on 2023/07/24. 6 | // 7 | 8 | import Foundation 9 | 10 | struct CurationProductsEntity: Decodable { 11 | let curationProducts: [CurationEntity] 12 | } 13 | 14 | struct CurationEntity: Decodable, Hashable { 15 | let title: String 16 | let subTitle: String 17 | let products: [ProductDetailEntity] 18 | } 19 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Entity/Products/SortEntity.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SortEntity.swift 3 | // Pyonsnal-Color 4 | // 5 | // Created by 김인호 on 2023/07/08. 6 | // 7 | 8 | struct SortEntity: Decodable { 9 | let isEmpty: Bool 10 | let unsorted: Bool 11 | let isSorted: Bool 12 | 13 | private enum CodingKeys: String, CodingKey { 14 | case isEmpty = "empty" 15 | case unsorted 16 | case isSorted = "sorted" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Extension/UITextField+.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UITextField+.swift 3 | // Pyonsnal-Color 4 | // 5 | // Created by 조소정 on 11/20/23. 6 | // 7 | 8 | import UIKit 9 | 10 | extension UITextField { 11 | func addLeftPaddingView(point: Int) { 12 | let height = Int(self.frame.size.height) 13 | self.leftView = UIView(frame: CGRect(x: 0, y: 0, width: point, height: height)) 14 | self.leftViewMode = .always 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Networking/NetworkRequestable.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NetworkRequestable.swift 3 | // Pyonsnal-Color 4 | // 5 | // Created by 조소정 on 2023/07/03. 6 | // 7 | 8 | import Combine 9 | import Foundation 10 | import Alamofire 11 | 12 | protocol NetworkRequestable { 13 | func request( 14 | _ urlRequest: NetworkRequestBuilder, 15 | model: T.Type 16 | ) -> AnyPublisher, Never> 17 | } 18 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Resource/ImageAssetKind/ImageAssetKind+HeaderView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ImageAssetKind+HeaderView.swift 3 | // Pyonsnal-Color 4 | // 5 | // Created by 김진우 on 2023/07/02. 6 | // 7 | 8 | import UIKit 9 | 10 | extension ImageAssetKind { 11 | enum HeaderView: String { 12 | case iconBack = "icon_back" 13 | 14 | var image: UIImage? { 15 | return .init(named: self.rawValue) 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Entity/Curation/EventImageEntity.swift: -------------------------------------------------------------------------------- 1 | // 2 | // EventImageEntity.swift 3 | // Pyonsnal-Color 4 | // 5 | // Created by 김인호 on 12/26/23. 6 | // 7 | 8 | import Foundation 9 | 10 | struct EventImageEntity: Decodable, Hashable { 11 | let title: String 12 | let bannerDetails: [EventBannerDetailEntity] 13 | 14 | private enum CodingKeys: String, CodingKey { 15 | case title 16 | case bannerDetails 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Resource/Assets.xcassets/Icon/icon_delete_medium.imageset/icon_delete(m).svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Resource/Constants/Url/TermsOfUseUrl.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TermsOfUseUrl.swift 3 | // Pyonsnal-Color 4 | // 5 | // Created by 조소정 on 2023/07/07. 6 | // 7 | 8 | import Foundation 9 | 10 | struct TermsOfUseUrl { 11 | static let use = "https://pentagonal-roquefort-ccf.notion.site/d6b586f765a94348b3130430c22a6e48?pvs=4" 12 | static let privateInfo = "https://pentagonal-roquefort-ccf.notion.site/fc827743cd4a481298baa5435386c134?pvs=4" 13 | } 14 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Entity/Products/ProductDetailSectionItem.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ProductDetailSectionItem.swift 3 | // Pyonsnal-Color 4 | // 5 | // Created by 김진우 on 2023/09/17. 6 | // 7 | 8 | import Foundation 9 | 10 | enum ProductDetailSectionItem { 11 | case image(imageURL: URL) 12 | case information(product: ProductDetailEntity) 13 | case reviewWrite(score: Double, reviewsCount: Int, sortItem: FilterItemEntity) 14 | case review(productReview: ReviewEntity) 15 | } 16 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Resource/Assets.xcassets/Profile/profile_plus.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "profile_plus@3x.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Extension/CALayer+.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CALayer+.swift 3 | // Pyonsnal-Color 4 | // 5 | // Created by 김인호 on 2023/06/11. 6 | // 7 | 8 | import UIKit 9 | 10 | extension CALayer { 11 | func applyShadow(color: UIColor, alpha: Float, width: CGFloat, height: CGFloat, blur: CGFloat) { 12 | shadowColor = color.cgColor 13 | shadowOpacity = alpha 14 | shadowOffset = CGSize(width: width, height: height) 15 | shadowRadius = blur 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Pyonsnal-Color.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.developer.applesignin 6 | 7 | Default 8 | 9 | com.apple.developer.associated-domains 10 | 11 | applinks:pyonsnalcolor.store 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Resource/Assets.xcassets/Common/icon_like_selected.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "icon_like_selected.svg", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Resource/ImageAssetKind/ImageAssetKind+Profile.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ImageAssetKind+Profile.swift 3 | // Pyonsnal-Color 4 | // 5 | // Created by 조소정 on 11/16/23. 6 | // 7 | 8 | import UIKit 9 | 10 | extension ImageAssetKind { 11 | enum Profile: String { 12 | case profileEdit = "profile_edit" 13 | case profilePlus = "profile_plus" 14 | 15 | var image: UIImage? { 16 | return .init(named: self.rawValue) 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Service/AppInfoService.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppInfoService.swift 3 | // Pyonsnal-Color 4 | // 5 | // Created by 조소정 on 2023/06/27. 6 | // 7 | 8 | import Foundation 9 | 10 | final class AppInfoService { 11 | private init() {} 12 | static let shared = AppInfoService() 13 | 14 | var appVersion: String? = { 15 | let appVersion = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String 16 | return appVersion 17 | }() 18 | 19 | } 20 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Resource/Assets.xcassets/Common/icon_like_unselected.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "icon_like_unselected.svg", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Resource/Assets.xcassets/Review/icon_thumbsDown_filled.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "icon_thumbsDown_filled.svg", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Resource/Assets.xcassets/Review/icon_thumbsUp_filled.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "icon_thumbsUp_filled.svg", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Resource/Assets.xcassets/Review/icon_thumbsUp_unfilled.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "icon_thumbsUp_unfilled.svg", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Resource/Assets.xcassets/TabBar/icon_tab_favorite_selected.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "icon_tab_selected.svg", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Resource/Assets.xcassets/Review/icon_thumbsDown_unfilled.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "icon_thumbsDown_unfilled.svg", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Resource/Assets.xcassets/TabBar/icon_tab_favorite_unselected.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "icon_tab_unselected.svg", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/3rdParty/SnapKit+.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SnapKit+.swift 3 | // Pyonsnal-Color 4 | // 5 | // Created by 김진우 on 2023/06/24. 6 | // 7 | 8 | import SnapKit 9 | 10 | extension ConstraintMakerEditable { 11 | @discardableResult 12 | func offset(_ spacing: Spacing) -> ConstraintMakerEditable { 13 | return offset(spacing.value) 14 | } 15 | 16 | @discardableResult 17 | func inset(_ spacing: Spacing) -> ConstraintMakerEditable { 18 | return inset(spacing.value) 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Resource/Assets.xcassets/Icon/logo.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "logo.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "filename" : "logo@2x.png", 10 | "idiom" : "universal", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "filename" : "logo@3x.png", 15 | "idiom" : "universal", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "author" : "xcode", 21 | "version" : 1 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Resource/ImageAssetKind/ImageAsset+Filter.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ImageAsset+Filter.swift 3 | // Pyonsnal-Color 4 | // 5 | // Created by 조소정 on 2023/07/22. 6 | // 7 | 8 | import UIKit 9 | 10 | extension ImageAssetKind { 11 | enum Filter: String { 12 | case refreshFilter = "icon_filter_refresh" 13 | case sortFilter = "icon_filter_view" 14 | case filter = "icon_filter" 15 | 16 | var image: UIImage? { 17 | return .init(named: self.rawValue) 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/ProfileHome/SettingInfo.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SettingInfo.swift 3 | // Pyonsnal-Color 4 | // 5 | // Created by 조소정 on 2023/07/12. 6 | // 7 | 8 | import Foundation 9 | 10 | struct SettingInfo { 11 | let title: String 12 | 13 | var infoUrl: Setting? 14 | 15 | enum Setting: String { 16 | case teams 17 | var urlString: String? { 18 | switch self { 19 | case .teams: 20 | return ProfileUrl.team 21 | } 22 | 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Resource/Assets.xcassets/Profile/profile_edit.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "edit_24.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "filename" : "edit_24@2x.png", 10 | "idiom" : "universal", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "filename" : "edit_24@3x.png", 15 | "idiom" : "universal", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "author" : "xcode", 21 | "version" : 1 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Resource/Assets.xcassets/Icon/icon_header.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "Frame 8907.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "filename" : "Frame 8907@2x.png", 10 | "idiom" : "universal", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "filename" : "Frame 8907@3x.png", 15 | "idiom" : "universal", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "author" : "xcode", 21 | "version" : 1 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Resource/Assets.xcassets/Icon/icon_search.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "icon_search.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "filename" : "icon_search@2x.png", 10 | "idiom" : "universal", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "filename" : "icon_search@3x.png", 15 | "idiom" : "universal", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "author" : "xcode", 21 | "version" : 1 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Entity/Products/PageableEntity.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PageableEntity.swift 3 | // Pyonsnal-Color 4 | // 5 | // Created by 김인호 on 2023/07/08. 6 | // 7 | 8 | struct PageableEntity: Decodable { 9 | let offset: Int 10 | let pageSize: Int 11 | let isPaged: Bool 12 | let pageNumber: Int 13 | let isUnpaged: Bool 14 | 15 | private enum CodingKeys: String, CodingKey { 16 | case offset 17 | case pageSize 18 | case isPaged = "paged" 19 | case pageNumber 20 | case isUnpaged = "unpaged" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Resource/Assets.xcassets/Splash/image_splash.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "image_splash.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "filename" : "image_splash@2x.png", 10 | "idiom" : "universal", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "filename" : "image_splash@3x.png", 15 | "idiom" : "universal", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "author" : "xcode", 21 | "version" : 1 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Resource/Constants/CommonConstants.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CommonConstants.swift 3 | // Pyonsnal-Color 4 | // 5 | // Created by 조소정 on 2023/07/13. 6 | // 7 | 8 | import Foundation 9 | 10 | struct CommonConstants { 11 | static let convenienceStore: [String] = ["CU", "GS25", "Emart24", "7-Eleven"] 12 | static let eventSectionHeaderTitle = "이달의 이벤트 💌" 13 | static let productSectionHeaderTitle = "차별화 상품 🍙" 14 | static let itemSectionHeaderTitle = "행사 상품 🎁" 15 | static let productHomeStore: [String] = ["PICK!"] + Self.convenienceStore 16 | } 17 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Resource/ImageAssetKind/ImageAssetKind+StoreTag.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ImageAssetKind+StoreTag.swift 3 | // Pyonsnal-Color 4 | // 5 | // Created by 조소정 on 2023/07/03. 6 | // 7 | 8 | import UIKit 9 | 10 | extension ImageAssetKind { 11 | enum StoreTag: String { 12 | case sevenEleven = "tag_store_7eleven" 13 | case cu = "tag_store_CU" 14 | case gs25 = "tag_store_GS" 15 | case emart24 = "tag_store_emart24" 16 | 17 | var image: UIImage? { 18 | return .init(named: self.rawValue) 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Common/Protocol/ProductListDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ProductListDelegate.swift 3 | // Pyonsnal-Color 4 | // 5 | // Created by 김인호 on 2023/07/09. 6 | // 7 | 8 | import Foundation 9 | 10 | protocol ProductListDelegate: FavoriteHomeDelegate { 11 | func didLoadPageList(store: ConvenienceStore) 12 | func refreshByPull() 13 | func didSelect(with product: ProductDetailEntity?) 14 | 15 | func deleteKeywordFilter(_ filter: FilterItemEntity) 16 | func refreshFilterButton() 17 | func didAppearProductList() 18 | func didFinishUpdateSnapshot() 19 | } 20 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Extension/UILabel+.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UILabel+.swift 3 | // Pyonsnal-Color 4 | // 5 | // Created by 김인호 on 2023/09/27. 6 | // 7 | 8 | import UIKit 9 | 10 | extension UILabel { 11 | func addAttributedString(newText: String,font: UIFont?, color: UIColor?) { 12 | guard let text = self.text else { return } 13 | 14 | let attributedString = NSMutableAttributedString(string: text) 15 | attributedString.appendAttributes(string: newText, font: font, color: color) 16 | 17 | self.attributedText = attributedString 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Resource/ImageAssetKind/ImageAssetKind+StoreIcon.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ImageAssetKind+StoreIcon.swift 3 | // Pyonsnal-Color 4 | // 5 | // Created by 김진우 on 2023/07/02. 6 | // 7 | 8 | import UIKit 9 | 10 | extension ImageAssetKind { 11 | enum StoreIcon: String { 12 | case sevenEleven = "store_logo_7-eleven" 13 | case cu = "store_logo_CU" 14 | case emart24 = "store_logo_emart24" 15 | case gs25 = "store_logo_GS" 16 | 17 | var image: UIImage? { 18 | return .init(named: self.rawValue) 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Resource/Assets.xcassets/Icon/icon_pyonsnalColor.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "icon_pyonsnalColor.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "filename" : "icon_pyonsnalColor@2x.png", 10 | "idiom" : "universal", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "filename" : "icon_pyonsnalColor@3x.png", 15 | "idiom" : "universal", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "author" : "xcode", 21 | "version" : 1 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Common/Interface/CommonProduct.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CommonProduct.swift 3 | // Pyonsnal-Color 4 | // 5 | // Created by 조소정 on 2023/09/03. 6 | // 7 | 8 | import UIKit 9 | 10 | // MARK: - Interface 11 | enum CommonProduct { 12 | enum Size { 13 | static let titleNavigationBarHeight: CGFloat = 48 14 | static let storeHeight: CGFloat = 44 15 | static let storeCollectionViewSeparatorHeight: CGFloat = 1 16 | static let filterMargin: UIEdgeInsets = .init(top: 12, left: 15, bottom: 12, right: 15) 17 | static let filterHeight: CGFloat = 56 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Common/UserEvent/FoodChemistryEvent/FoodChemistryCell.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FoodChemistryCell.swift 3 | // Pyonsnal-Color 4 | // 5 | // Created by 김인호 on 1/23/24. 6 | // 7 | 8 | import UIKit 9 | 10 | final class FoodChemistryCell: UICollectionViewCell { 11 | 12 | override init(frame: CGRect) { 13 | super.init(frame: frame) 14 | 15 | configureView() 16 | } 17 | 18 | required init?(coder: NSCoder) { 19 | fatalError("init(coder:) has not been implemented") 20 | } 21 | 22 | private func configureView() { 23 | backgroundColor = .clear 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Common/Protocol/CommonProductPageViewControllerRenderable.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CommonProductPageViewControllerRenderable.swift 3 | // Pyonsnal-Color 4 | // 5 | // Created by 조소정 on 2023/09/09. 6 | // 7 | 8 | import Foundation 9 | 10 | protocol CommonProductPageViewControllerRenderable: FavoriteHomeDelegate { 11 | func updateSelectedStoreCell(index: Int) 12 | func didChangeStore(to store: ConvenienceStore) 13 | func didSelect(with brandProduct: ProductDetailEntity) 14 | func deleteKeywordFilter(_ filter: FilterItemEntity) 15 | func didTapRefreshFilterButton() 16 | func didFinishUpdateSnapshot() 17 | } 18 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Resource/Assets.xcassets/LineIconSmall/icon_filter_view.imageset/icon_filter_view.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Extension/DateFormatter+.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DateFormatter+.swift 3 | // Pyonsnal-Color 4 | // 5 | // Created by 김인호 on 2023/06/30. 6 | // 7 | 8 | import Foundation 9 | 10 | extension DateFormatter { 11 | static let notificationFormatter: DateFormatter = { 12 | let dateFormatter = DateFormatter() 13 | dateFormatter.dateFormat = "yyyy.mm.dd" 14 | return dateFormatter 15 | }() 16 | 17 | static let productLastUpdateFormatter: DateFormatter = { 18 | let dateFormatter = DateFormatter() 19 | dateFormatter.dateFormat = "yy.mm.dd" 20 | return dateFormatter 21 | }() 22 | } 23 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Resource/Assets.xcassets/Filter/icon_filter_view.imageset/icon_filter_view.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Entity/Filter/FilterCellItem.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FilterCellItem.swift 3 | // Pyonsnal-Color 4 | // 5 | // Created by 조소정 on 2023/07/25. 6 | // 7 | 8 | import Foundation 9 | 10 | struct FilterCellItem: Hashable { 11 | var filterUseType: FilterUseType = .category 12 | var filter: FilterEntity? 13 | 14 | static func == (lhs: FilterCellItem, rhs: FilterCellItem) -> Bool { 15 | return lhs.filter?.defaultText == rhs.filter?.defaultText && lhs.filter?.filterType == rhs.filter?.filterType && lhs.filter?.isSelected == rhs.filter?.isSelected 16 | } 17 | 18 | enum FilterUseType { 19 | case refresh 20 | case category 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Entity/Products/EventBannerEntity.swift: -------------------------------------------------------------------------------- 1 | // 2 | // EventBannerEntity.swift 3 | // Pyonsnal-Color 4 | // 5 | // Created by 김인호 on 2023/07/09. 6 | // 7 | 8 | import Foundation 9 | 10 | struct EventBannerEntity: Decodable, Hashable { 11 | let identifier: String 12 | let storeType: ConvenienceStore 13 | let thumbnailImageURL: String 14 | let imageURL: String? 15 | let title: String? 16 | let updatedTime: String 17 | 18 | private enum CodingKeys: String, CodingKey { 19 | case identifier = "id" 20 | case storeType 21 | case thumbnailImageURL = "thumbnailImage" 22 | case imageURL = "image" 23 | case title 24 | case updatedTime 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Extension/UITableView+.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UITableView+.swift 3 | // Pyonsnal-Color 4 | // 5 | // Created by 조소정 on 2023/06/27. 6 | // 7 | 8 | import UIKit 9 | 10 | extension UITableView { 11 | func register(_ cellClass: AnyClass) { 12 | register(cellClass.self, forCellReuseIdentifier: String(describing: cellClass.self)) 13 | } 14 | 15 | func dequeueReusableCell(for indexPath: IndexPath) -> T { 16 | guard let cell = self.dequeueReusableCell(withIdentifier: T.identifier, for: indexPath) as? T else { 17 | fatalError("Unexpected identifier : \(T.identifier)") 18 | } 19 | return cell 20 | } 21 | } 22 | 23 | extension UITableViewCell: ItemIdentifier {} 24 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Resource/Assets.xcassets/Common/icon_like_unselected.imageset/icon_like_unselected.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/TermsOfUse/SubTerms.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TermsOfUse.swift 3 | // Pyonsnal-Color 4 | // 5 | // Created by 조소정 on 2023/07/07. 6 | // 7 | 8 | import Foundation 9 | 10 | struct SubTerms { 11 | let title: String 12 | var hasNextPage: Bool = false 13 | var type: TermsOfUse 14 | 15 | enum TermsOfUse { 16 | case age 17 | case use 18 | case privateInfo 19 | 20 | var urlString: String? { 21 | switch self { 22 | case .age: 23 | return nil 24 | case .use: 25 | return TermsOfUseUrl.use 26 | case .privateInfo: 27 | return TermsOfUseUrl.privateInfo 28 | } 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Resource/Assets.xcassets/TabBar/icon_tab_favorite_unselected.imageset/icon_tab_unselected.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Extension/NSMutableAttributedString+.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NSMutableAttributedString+.swift 3 | // Pyonsnal-Color 4 | // 5 | // Created by 조소정 on 2023/06/14. 6 | // 7 | 8 | import UIKit 9 | 10 | extension NSMutableAttributedString { 11 | 12 | /// 특정 문자열의 color와 font 속성을 부여합니다. 13 | /// - Parameter font: 적용할 font 속성 값 14 | /// - Parameter color: 적용할 color 속성 값 15 | func appendAttributes( 16 | string: String, 17 | font: UIFont?, 18 | color: UIColor? 19 | ) { 20 | var attr: [NSAttributedString.Key: Any] = [:] 21 | attr[.font] = font 22 | attr[.foregroundColor] = color 23 | self.append(NSAttributedString( 24 | string: string, 25 | attributes: attr) 26 | ) 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Resource/Assets.xcassets/TabBar/icon_tab_favorite_selected.imageset/icon_tab_selected.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Resource/Assets.xcassets/Others/icon_revert.imageset/icon_revert.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Resource/Assets.xcassets/Common/icon_like_selected.imageset/icon_like_selected.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Resource/Assets.xcassets/Filter/icon_filter_refresh.imageset/icon_revert.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Entity/Products/ProductConvertable.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ProductConvertable.swift 3 | // Pyonsnal-Color 4 | // 5 | // Created by 김인호 on 2023/07/08. 6 | // 7 | 8 | import UIKit 9 | 10 | protocol ProductConvertable: Decodable, Hashable { 11 | var productId: String { get } 12 | var imageURL: URL { get } 13 | var storeType: ConvenienceStore { get } 14 | var updatedTime: String { get } 15 | var name: String { get } 16 | var price: String { get } 17 | var description: String? { get } 18 | var eventType: EventTag? { get } 19 | var productType: ProductType { get } 20 | var isFavorite: Bool? { get } 21 | var isNew: Bool? { get } 22 | } 23 | 24 | extension ProductConvertable { 25 | func hash(into hasher: inout Hasher) { 26 | hasher.combine(productId) 27 | } 28 | } 29 | 30 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Resource/Assets.xcassets/LineIconLarge/icon_share.imageset/icon_share.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Common/Protocol/TopCollectionViewDatasource.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TopCollectionViewDatasource.swift 3 | // Pyonsnal-Color 4 | // 5 | // Created by 조소정 on 2023/07/23. 6 | // 7 | 8 | import UIKit 9 | 10 | class TopCollectionViewDatasource { 11 | enum SectionType: Hashable { 12 | case convenienceStore(store: [String]) 13 | } 14 | 15 | enum ItemType: Hashable { 16 | case convenienceStore(storeName: String) 17 | } 18 | 19 | enum FilterSection: Hashable { 20 | case filter 21 | } 22 | 23 | enum FilterItem: Hashable { 24 | case filter(filterItem: FilterCellItem) 25 | } 26 | 27 | typealias StoreDataSource = UICollectionViewDiffableDataSource 28 | typealias FilterDataSource = UICollectionViewDiffableDataSource 29 | } 30 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Resource/Assets.xcassets/Review/star_large.imageset/filled=unfilled, size=L.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Resource/Assets.xcassets/Review/star_medium.imageset/filled=unfilled, size=m.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Entity/EventTag.swift: -------------------------------------------------------------------------------- 1 | // 2 | // EventTag.swift 3 | // Pyonsnal-Color 4 | // 5 | // Created by 김진우 on 2023/07/04. 6 | // 7 | 8 | enum EventTag: String, Decodable { 9 | case discount = "DISCOUNT" 10 | case freebie = "GIFT" 11 | case onePlusOne = "ONE_TO_ONE" 12 | case twoPlusOne = "TWO_TO_ONE" 13 | case threePlusOne = "THREE_TO_ONE" 14 | case none = "NONE" 15 | } 16 | 17 | extension EventTag { 18 | var name: String? { 19 | switch self { 20 | case .discount: 21 | return "할인" 22 | case .freebie: 23 | return "증정" 24 | case .onePlusOne: 25 | return "1+1" 26 | case .twoPlusOne: 27 | return "2+1" 28 | case .threePlusOne: 29 | return "3+1" 30 | case .none: 31 | return "" 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Resource/ImageAssetKind/ImageAssetKind+TabBar.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ImageAssetKind+TabBar.swift 3 | // Pyonsnal-Color 4 | // 5 | // Created by 김진우 on 2023/07/27. 6 | // 7 | 8 | import UIKit 9 | 10 | extension ImageAssetKind { 11 | enum TabBar: String { 12 | case homeSelected = "icon_tab_home_selected" 13 | case homeUnselected = "icon_tab_home_unselected" 14 | case mySelected = "icon_tab_my_selected" 15 | case myUnselected = "icon_tab_my_unselected" 16 | case favoriteSelected = "icon_tab_favorite_selected" 17 | case favoriteUnSelected = "icon_tab_favorite_unselected" 18 | case eventSelected = "icon_tab_sale_selected" 19 | case eventUnselected = "icon_tab_sale_unselected" 20 | 21 | var image: UIImage? { 22 | return .init(named: self.rawValue) 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Common/Protocol/FilterRenderable.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FilterRenderable.swift 3 | // Pyonsnal-Color 4 | // 5 | // Created by 조소정 on 2023/08/26. 6 | // 7 | 8 | import Foundation 9 | 10 | protocol FilterRenderable { 11 | var filterStateManager: FilterStateManager? { get set } 12 | var filterDataEntity: FilterDataEntity? { get } 13 | var selectedFilterCodeList: [Int] { get } 14 | var selectedFilterKeywordList: [FilterItemEntity]? { get } 15 | var isNeedToShowRefreshFilterCell: Bool { get } 16 | func didSelectFilter(_ filterEntity: FilterEntity?) 17 | func didTapRefreshFilterCell() 18 | func requestwithUpdatedKeywordFilter() 19 | func initializeFilterState() 20 | func updateFiltersState(with filters: [FilterItemEntity], type: FilterType) 21 | func deleteKeywordFilter(_ filter: FilterItemEntity) 22 | func resetFilterState() 23 | } 24 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Entity/Curation/EventBannerDetailEntity.swift: -------------------------------------------------------------------------------- 1 | // 2 | // EventBannerDetailEntity.swift 3 | // Pyonsnal-Color 4 | // 5 | // Created by 김인호 on 1/19/24. 6 | // 7 | 8 | import UIKit 9 | 10 | struct EventBannerDetailEntity: Decodable, Hashable { 11 | let userEvent: UserEvent 12 | let thumbnailImage: String 13 | let detailImage: String 14 | let links: [String] 15 | 16 | enum CodingKeys: CodingKey { 17 | case userEvent 18 | case thumbnailImage 19 | case detailImage 20 | case links 21 | } 22 | } 23 | 24 | extension EventBannerDetailEntity { 25 | /* 26 | var view: UIView? { 27 | switch userEvent { 28 | case .foodChemistryEvent: 29 | return FoodChemistryEventView(links: links) 30 | case .launchingEvent: 31 | return nil 32 | } 33 | } 34 | */ 35 | } 36 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Resource/Assets.xcassets/Review/icon_thumbsUp_unfilled.imageset/icon_thumbsUp_unfilled.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Extension/CGFloat+.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CGFloat+.swift 3 | // Pyonsnal-Color 4 | // 5 | // Created by 김진우 on 2023/06/24. 6 | // 7 | 8 | import Foundation 9 | 10 | extension CGFloat { 11 | static let spacing2: CGFloat = Spacing.spacing2.value 12 | static let spacing4: CGFloat = Spacing.spacing4.value 13 | static let spacing6: CGFloat = Spacing.spacing6.value 14 | static let spacing8: CGFloat = Spacing.spacing8.value 15 | static let spacing10: CGFloat = Spacing.spacing10.value 16 | static let spacing12: CGFloat = Spacing.spacing12.value 17 | static let spacing16: CGFloat = Spacing.spacing16.value 18 | static let spacing20: CGFloat = Spacing.spacing20.value 19 | static let spacing24: CGFloat = Spacing.spacing24.value 20 | static let spacing32: CGFloat = Spacing.spacing32.value 21 | static let spacing40: CGFloat = Spacing.spacing40.value 22 | } 23 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/ReviewPopup/ReviewPopupRouter.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ReviewPopupRouter.swift 3 | // Pyonsnal-Color 4 | // 5 | // Created by 김인호 on 2023/09/21. 6 | // 7 | 8 | import ModernRIBs 9 | 10 | protocol ReviewPopupInteractable: Interactable { 11 | var router: ReviewPopupRouting? { get set } 12 | var listener: ReviewPopupListener? { get set } 13 | } 14 | 15 | protocol ReviewPopupViewControllable: ViewControllable { 16 | } 17 | 18 | final class ReviewPopupRouter: ViewableRouter, 20 | ReviewPopupRouting { 21 | 22 | override init( 23 | interactor: ReviewPopupInteractable, 24 | viewController: ReviewPopupViewControllable 25 | ) { 26 | super.init(interactor: interactor, viewController: viewController) 27 | interactor.router = self 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Entity/Products/ProductPageEntity.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ProductPageEntity.swift 3 | // Pyonsnal-Color 4 | // 5 | // Created by 김인호 on 2023/07/08. 6 | // 7 | 8 | struct ProductPageEntity: Decodable { 9 | let totalPages: Int? 10 | let totalElements: Int? 11 | let size: Int 12 | let content: [ProductDetailEntity] 13 | let number: Int 14 | let sort: SortEntity 15 | let isFirst: Bool 16 | let isLast: Bool 17 | let numberOfElements: Int 18 | let pageable: PageableEntity 19 | let isEmpty: Bool 20 | 21 | private enum CodingKeys: String, CodingKey { 22 | case totalPages 23 | case totalElements 24 | case size 25 | case content 26 | case number 27 | case sort 28 | case isFirst = "first" 29 | case isLast = "last" 30 | case numberOfElements 31 | case pageable 32 | case isEmpty = "empty" 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Resource/Assets.xcassets/TabBar/icon_tab_my_unselected.imageset/icon_tab_my_unselected.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/CommonWeb/CommonWebRouter.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CommonWebRouter.swift 3 | // Pyonsnal-Color 4 | // 5 | // Created by 조소정 on 2023/07/07. 6 | // 7 | 8 | import ModernRIBs 9 | 10 | protocol CommonWebInteractable: Interactable { 11 | var router: CommonWebRouting? { get set } 12 | var listener: CommonWebListener? { get set } 13 | } 14 | 15 | protocol CommonWebViewControllable: ViewControllable { 16 | // TODO: Declare methods the router invokes to manipulate the view hierarchy. 17 | } 18 | 19 | final class CommonWebRouter: ViewableRouter, CommonWebRouting { 20 | 21 | // TODO: Constructor inject child builder protocols to allow building children. 22 | override init(interactor: CommonWebInteractable, viewController: CommonWebViewControllable) { 23 | super.init(interactor: interactor, viewController: viewController) 24 | interactor.router = self 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Common/Combine/ControlPublisher.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ControlPublisher.swift 3 | // Pyonsnal-Color 4 | // 5 | // Created by 조소정 on 2023/09/06. 6 | // 7 | 8 | import UIKit 9 | import Combine 10 | 11 | struct ControlPublisher: Publisher { 12 | typealias Output = UIControl 13 | typealias Failure = Never 14 | 15 | private let control: UIControl 16 | private let controlEvent: UIControl.Event 17 | 18 | init(control: UIControl, controlEvent: UIControl.Event) { 19 | self.control = control 20 | self.controlEvent = controlEvent 21 | } 22 | 23 | func receive(subscriber: S) where S: Subscriber, Never == S.Failure, UIControl == S.Input { 24 | let subscription = ControlSubscription( 25 | subscriber: subscriber, 26 | control: control, 27 | controlEvent: controlEvent 28 | ) 29 | subscriber.receive(subscription: subscription) 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/LoginPopup/LoginPopupRouter.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LoginPopupRouter.swift 3 | // Pyonsnal-Color 4 | // 5 | // Created by 김인호 on 1/31/24. 6 | // 7 | 8 | import ModernRIBs 9 | 10 | protocol LoginPopupInteractable: Interactable { 11 | var router: LoginPopupRouting? { get set } 12 | var listener: LoginPopupListener? { get set } 13 | } 14 | 15 | protocol LoginPopupViewControllable: ViewControllable { 16 | // TODO: Declare methods the router invokes to manipulate the view hierarchy. 17 | } 18 | 19 | final class LoginPopupRouter: ViewableRouter, LoginPopupRouting { 20 | 21 | // TODO: Constructor inject child builder protocols to allow building children. 22 | override init(interactor: LoginPopupInteractable, viewController: LoginPopupViewControllable) { 23 | super.init(interactor: interactor, viewController: viewController) 24 | interactor.router = self 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Common/Combine/GesturePublisher.swift: -------------------------------------------------------------------------------- 1 | // 2 | // GesturePublisher.swift 3 | // Pyonsnal-Color 4 | // 5 | // Created by 조소정 on 2023/09/06. 6 | // 7 | 8 | import UIKit 9 | import Combine 10 | 11 | struct GesturePublisher: Publisher { 12 | 13 | typealias Output = UIGestureRecognizer 14 | typealias Failure = Never 15 | 16 | private weak var targetView: UIView? 17 | private let gestureRecognizer: UIGestureRecognizer 18 | 19 | init(targetView: UIView, gestureRecognizer: UIGestureRecognizer) { 20 | self.targetView = targetView 21 | self.gestureRecognizer = gestureRecognizer 22 | } 23 | 24 | func receive(subscriber: S) where S : Subscriber, Never == S.Failure, UIGestureRecognizer == S.Input { 25 | let subscription = GestureSubscription(subscriber: subscriber, view: targetView, gestureRecognizer: gestureRecognizer) 26 | subscriber.receive(subscription: subscription) 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/ProfileEdit/ProfileEditRouter.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ProfileEditRouter.swift 3 | // Pyonsnal-Color 4 | // 5 | // Created by 조소정 on 11/16/23. 6 | // 7 | 8 | import ModernRIBs 9 | 10 | protocol ProfileEditInteractable: Interactable { 11 | var router: ProfileEditRouting? { get set } 12 | var listener: ProfileEditListener? { get set } 13 | } 14 | 15 | protocol ProfileEditViewControllable: ViewControllable { 16 | // TODO: Declare methods the router invokes to manipulate the view hierarchy. 17 | } 18 | 19 | final class ProfileEditRouter: ViewableRouter, ProfileEditRouting { 20 | 21 | // TODO: Constructor inject child builder protocols to allow building children. 22 | override init(interactor: ProfileEditInteractable, viewController: ProfileEditViewControllable) { 23 | super.init(interactor: interactor, viewController: viewController) 24 | interactor.router = self 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/LogoutPopup/LogoutPopupRouter.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LogoutPopupRouter.swift 3 | // Pyonsnal-Color 4 | // 5 | // Created by 김인호 on 2023/07/07. 6 | // 7 | 8 | import ModernRIBs 9 | 10 | protocol LogoutPopupInteractable: Interactable { 11 | var router: LogoutPopupRouting? { get set } 12 | var listener: LogoutPopupListener? { get set } 13 | } 14 | 15 | protocol LogoutPopupViewControllable: ViewControllable { 16 | // TODO: Declare methods the router invokes to manipulate the view hierarchy. 17 | } 18 | 19 | final class LogoutPopupRouter: ViewableRouter, LogoutPopupRouting { 20 | 21 | // TODO: Constructor inject child builder protocols to allow building children. 22 | override init(interactor: LogoutPopupInteractable, viewController: LogoutPopupViewControllable) { 23 | super.init(interactor: interactor, viewController: viewController) 24 | interactor.router = self 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Common/ReusableView/LoadingReusableView+ViewHolder.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LoadingReusableView+ViewHolder.swift 3 | // Pyonsnal-Color 4 | // 5 | // Created by 김진우 on 2023/07/23. 6 | // 7 | 8 | import UIKit 9 | 10 | extension LoadingReusableView { 11 | final class ViewHolder: ViewHolderable { 12 | // MARK: - UI Component 13 | let indicatorView: UIActivityIndicatorView = { 14 | let indicatorView = UIActivityIndicatorView() 15 | indicatorView.color = .gray500 16 | return indicatorView 17 | }() 18 | 19 | // MARK: - Interface 20 | func place(in view: UIView) { 21 | view.addSubview(indicatorView) 22 | } 23 | 24 | func configureConstraints(for view: UIView) { 25 | indicatorView.snp.makeConstraints { make in 26 | make.size.equalTo(44) 27 | make.center.equalToSuperview() 28 | } 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Resource/Assets.xcassets/Review/icon_thumbsUp_filled.imageset/icon_thumbsUp_filled.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Entity/Products/BrandProductEntity.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BrandProductEntity.swift 3 | // Pyonsnal-Color 4 | // 5 | // Created by 김인호 on 2023/07/08. 6 | // 7 | 8 | import Foundation 9 | 10 | struct BrandProductEntity: Decodable, ProductConvertable, Hashable { 11 | let productId: String 12 | let imageURL: URL 13 | let storeType: ConvenienceStore 14 | let updatedTime: String 15 | let name: String 16 | let price: String 17 | let description: String? 18 | let eventType: EventTag? 19 | let productType: ProductType 20 | let isFavorite: Bool? 21 | let isNew: Bool? 22 | 23 | private enum CodingKeys: String, CodingKey { 24 | case productId = "id" 25 | case imageURL = "image" 26 | case storeType 27 | case updatedTime 28 | case name 29 | case price 30 | case description 31 | case eventType 32 | case productType 33 | case isFavorite 34 | case isNew 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/ProductFilter/ProductFilterRouter.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ProductFilterRouter.swift 3 | // Pyonsnal-Color 4 | // 5 | // Created by 김인호 on 2023/07/25. 6 | // 7 | 8 | import ModernRIBs 9 | 10 | protocol ProductFilterInteractable: Interactable { 11 | var router: ProductFilterRouting? { get set } 12 | var listener: ProductFilterListener? { get set } 13 | } 14 | 15 | protocol ProductFilterViewControllable: ViewControllable { 16 | // TODO: Declare methods the router invokes to manipulate the view hierarchy. 17 | } 18 | 19 | final class ProductFilterRouter: ViewableRouter, ProductFilterRouting { 20 | 21 | // TODO: Constructor inject child builder protocols to allow building children. 22 | override init(interactor: ProductFilterInteractable, viewController: ProductFilterViewControllable) { 23 | super.init(interactor: interactor, viewController: viewController) 24 | interactor.router = self 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Resource/ImageAssetKind/ImageAssetKind+Icon.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ImageAssetKind+Icon.swift 3 | // Pyonsnal-Color 4 | // 5 | // Created by 김진우 on 2023/07/07. 6 | // 7 | 8 | import UIKit 9 | 10 | extension ImageAssetKind { 11 | enum Icon: String { 12 | case iconHeader = "icon_header" 13 | case iconPyonsnalColor = "icon_pyonsnalColor" 14 | case iconSearch = "icon_search" 15 | case iconDelete = "icon_delete" 16 | case iconDeleteMedium = "icon_delete_medium" 17 | case iconCheck = "icon_check" 18 | case iconReview = "icon_review" 19 | case iconThumbsDownFilled = "icon_thumbsDown_filled" 20 | case iconThumbsDownUnfilled = "icon_thumbsDown_unfilled" 21 | case iconThumbsUpFilled = "icon_thumbsUp_filled" 22 | case iconThumbsUpUnfilled = "icon_thumbsUp_unfilled" 23 | } 24 | } 25 | 26 | extension ImageAssetKind.Icon { 27 | var image: UIImage? { 28 | return UIImage(named: self.rawValue) 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Resource/Assets.xcassets/Review/icon_thumbsDown_unfilled.imageset/icon_thumbsDown_unfilled.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/NotificationList/NotificationListRouter.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NotificationListRouter.swift 3 | // Pyonsnal-Color 4 | // 5 | // Created by 김인호 on 2023/06/27. 6 | // 7 | 8 | import ModernRIBs 9 | 10 | protocol NotificationListInteractable: Interactable { 11 | var router: NotificationListRouting? { get set } 12 | var listener: NotificationListListener? { get set } 13 | } 14 | 15 | protocol NotificationListViewControllable: ViewControllable { 16 | // TODO: Declare methods the router invokes to manipulate the view hierarchy. 17 | } 18 | 19 | final class NotificationListRouter: ViewableRouter, NotificationListRouting { 20 | 21 | // TODO: Constructor inject child builder protocols to allow building children. 22 | override init(interactor: NotificationListInteractable, viewController: NotificationListViewControllable) { 23 | super.init(interactor: interactor, viewController: viewController) 24 | interactor.router = self 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Resource/GoogleService-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | API_KEY 6 | AIzaSyApvMbQmUm1wedGAB0jFWEkc52A95uo07g 7 | GCM_SENDER_ID 8 | 251124560897 9 | PLIST_VERSION 10 | 1 11 | BUNDLE_ID 12 | kr.co.yapp.22th.pyonsnalcolor 13 | PROJECT_ID 14 | pyonsnalcolor-6ff5d 15 | STORAGE_BUCKET 16 | pyonsnalcolor-6ff5d.appspot.com 17 | IS_ADS_ENABLED 18 | 19 | IS_ANALYTICS_ENABLED 20 | 21 | IS_APPINVITE_ENABLED 22 | 23 | IS_GCM_ENABLED 24 | 25 | IS_SIGNIN_ENABLED 26 | 27 | GOOGLE_APP_ID 28 | 1:251124560897:ios:5312e33e8cfb72088efc00 29 | 30 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Resource/Assets.xcassets/Review/icon_thumbsDown_filled.imageset/icon_thumbsDown_filled.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/RootTabBar/RootTabBarComponent.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RootTabBarComponent.swift 3 | // Pyonsnal-Color 4 | // 5 | // Created by 김인호 on 2023/06/10. 6 | // 7 | 8 | import ModernRIBs 9 | 10 | final class RootTabBarComponent: Component { 11 | } 12 | 13 | extension RootTabBarComponent: ProductHomeDependency, 14 | EventHomeDependency, 15 | FavoriteHomeDependency, 16 | ProfileHomeDependency { 17 | 18 | var client: PyonsnalColorClient { 19 | return PyonsnalColorClient() 20 | } 21 | var userAuthService: UserAuthService { 22 | return UserAuthService(keyChainService: KeyChainService.shared) 23 | } 24 | 25 | var favoriteAPIService: FavoriteAPIService { 26 | return FavoriteAPIService(client: client, userAuthService: userAuthService) 27 | } 28 | 29 | var productAPIService: ProductAPIService { 30 | return ProductAPIService(client: client, userAuthService: userAuthService) 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Common/ReusableView/LoadingReusableView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LoadingReusableView.swift 3 | // Pyonsnal-Color 4 | // 5 | // Created by 김진우 on 2023/07/23. 6 | // 7 | 8 | import UIKit 9 | 10 | final class LoadingReusableView: UICollectionReusableView { 11 | 12 | // MARK: - Private Property 13 | private let viewHolder: ViewHolder = .init() 14 | 15 | // MARK: - Initailzer 16 | override init(frame: CGRect) { 17 | super.init(frame: frame) 18 | 19 | configureView() 20 | configureConstraint() 21 | configureUI() 22 | } 23 | 24 | required init?(coder: NSCoder) { 25 | fatalError("init(coder:) has not been implemented") 26 | } 27 | 28 | // MARK: - Private Method 29 | private func configureView() { 30 | viewHolder.place(in: self) 31 | } 32 | 33 | private func configureConstraint() { 34 | viewHolder.configureConstraints(for: self) 35 | } 36 | 37 | private func configureUI() { 38 | viewHolder.indicatorView.startAnimating() 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/LoggedIn/LoggedInInteractor.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LoggedInInteractor.swift 3 | // Pyonsnal-Color 4 | // 5 | // Created by 김인호 on 2023/06/10. 6 | // 7 | 8 | import ModernRIBs 9 | 10 | protocol LoggedInRouting: Routing { 11 | func cleanupViews() 12 | func attachRootTabBar() 13 | } 14 | 15 | protocol LoggedInListener: AnyObject { 16 | func routeToLoggedOutFromLoggedIn() 17 | func routeToLoggedInFromLoggedOut() 18 | } 19 | 20 | final class LoggedInInteractor: Interactor, LoggedInInteractable { 21 | 22 | weak var router: LoggedInRouting? 23 | weak var listener: LoggedInListener? 24 | 25 | override init() {} 26 | 27 | override func didBecomeActive() { 28 | super.didBecomeActive() 29 | } 30 | 31 | override func willResignActive() { 32 | super.willResignActive() 33 | router?.cleanupViews() 34 | } 35 | 36 | func routeToLoggedOut() { 37 | listener?.routeToLoggedOutFromLoggedIn() 38 | } 39 | 40 | func routeToLoggedIn() { 41 | listener?.routeToLoggedInFromLoggedOut() 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/ProductSearch/SortBottomSheet/ProductSearchSortBottomSheetRouter.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ProductSearchSortBottomSheetRouter.swift 3 | // Pyonsnal-Color 4 | // 5 | // Created by 김진우 on 2023/07/30. 6 | // 7 | 8 | import ModernRIBs 9 | 10 | protocol ProductSearchSortBottomSheetInteractable: Interactable { 11 | var router: ProductSearchSortBottomSheetRouting? { get set } 12 | var listener: ProductSearchSortBottomSheetListener? { get set } 13 | } 14 | 15 | protocol ProductSearchSortBottomSheetViewControllable: ViewControllable { 16 | // TODO: Declare methods the router invokes to manipulate the view hierarchy. 17 | } 18 | 19 | final class ProductSearchSortBottomSheetRouter: ViewableRouter, ProductSearchSortBottomSheetRouting { 20 | 21 | override init(interactor: ProductSearchSortBottomSheetInteractable, viewController: ProductSearchSortBottomSheetViewControllable) { 22 | super.init(interactor: interactor, viewController: viewController) 23 | interactor.router = self 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/DesignSystem/Spacing.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Spacing.swift 3 | // Pyonsnal-Color 4 | // 5 | // Created by 김진우 on 2023/06/24. 6 | // 7 | 8 | import UIKit 9 | 10 | struct Spacing { 11 | let value: CGFloat 12 | 13 | private init(value: CGFloat) { 14 | self.value = value 15 | } 16 | 17 | var negative: Spacing { 18 | Spacing(value: -value) 19 | } 20 | } 21 | 22 | extension Spacing { 23 | static let spacing2: Spacing = .init(value: 2) 24 | static let spacing4: Spacing = .init(value: 4) 25 | static let spacing6: Spacing = .init(value: 6) 26 | static let spacing8: Spacing = .init(value: 8) 27 | static let spacing10: Spacing = .init(value: 10) 28 | static let spacing12: Spacing = .init(value: 12) 29 | static let spacing16: Spacing = .init(value: 16) 30 | static let spacing20: Spacing = .init(value: 20) 31 | static let spacing24: Spacing = .init(value: 24) 32 | static let spacing28: Spacing = .init(value: 28) 33 | static let spacing32: Spacing = .init(value: 32) 34 | static let spacing40: Spacing = .init(value: 40) 35 | } 36 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Resource/Assets.xcassets/HeaderView/bellSimple.imageset/BellSimple.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Resource/Assets.xcassets/LineIconMedium/icon_alarm.imageset/icon_alarm.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Common/Footer/LineFooter.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LineFooter.swift 3 | // Pyonsnal-Color 4 | // 5 | // Created by 김진우 on 2023/09/10. 6 | // 7 | 8 | import UIKit 9 | 10 | import SnapKit 11 | 12 | final class LineFooter: UICollectionReusableView { 13 | 14 | // MARK: - UI Component 15 | 16 | private let contentView: UIView = { 17 | let view = UIView() 18 | view.backgroundColor = .gray100 19 | return view 20 | }() 21 | 22 | // MARK: - Initailzer 23 | 24 | override init(frame: CGRect) { 25 | super.init(frame: frame) 26 | 27 | configureViews() 28 | configureConstraints() 29 | } 30 | 31 | required init?(coder: NSCoder) { 32 | fatalError("init(coder:) has not been implemented") 33 | } 34 | 35 | // MARK: - Override Method 36 | 37 | private func configureViews() { 38 | addSubview(contentView) 39 | } 40 | 41 | private func configureConstraints() { 42 | contentView.snp.makeConstraints { make in 43 | make.edges.equalToSuperview() 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Extension/CombineCocoa+.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CombineCocoa+.swift 3 | // Pyonsnal-Color 4 | // 5 | // Created by 조소정 on 2023/09/06. 6 | // 7 | 8 | import UIKit 9 | import Combine 10 | 11 | extension UIButton { 12 | var tapPublisher: AnyPublisher { 13 | return controlPublisher(for: .touchUpInside) 14 | .map { _ in } 15 | .eraseToAnyPublisher() 16 | } 17 | } 18 | 19 | extension UITextField { 20 | var textPublisher: AnyPublisher { 21 | return controlPublisher(for: .editingChanged) 22 | .compactMap { $0 as? UITextField } 23 | .map { $0.text } 24 | .eraseToAnyPublisher() 25 | } 26 | } 27 | 28 | extension UIView { 29 | func gesturePublisher(with delegate: UIGestureRecognizerDelegate? = nil) -> GesturePublisher { 30 | /// 현재는 tapGesture가 default. 추후 다른 gesture가 필요로 되면 gestureRecognizer 확장하여 사용. 31 | let gestureRecognizer = UITapGestureRecognizer() 32 | gestureRecognizer.delegate = delegate 33 | return GesturePublisher(targetView: self, gestureRecognizer: gestureRecognizer) 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/ProductHome/Views/ReusableView/EventBannerHeaderView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // EventBannerHeaderView.swift 3 | // Pyonsnal-Color 4 | // 5 | // Created by 김인호 on 12/27/23. 6 | // 7 | 8 | import UIKit 9 | import SnapKit 10 | 11 | final class EventBannerHeaderView: UICollectionReusableView { 12 | private let titleLabel: UILabel = { 13 | let label = UILabel() 14 | label.font = .title2 15 | label.textColor = .black 16 | return label 17 | }() 18 | 19 | override init(frame: CGRect) { 20 | super.init(frame: .zero) 21 | 22 | configureLayout() 23 | } 24 | 25 | required init?(coder: NSCoder) { 26 | fatalError("init(coder:) has not been implemented") 27 | } 28 | 29 | func configureHeaderView(title: String) { 30 | titleLabel.text = title 31 | } 32 | 33 | private func configureLayout() { 34 | addSubview(titleLabel) 35 | 36 | titleLabel.snp.makeConstraints { make in 37 | make.leading.bottom.equalToSuperview() 38 | make.height.equalTo(titleLabel.font.customLineHeight) 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Entity/Filter/FilterType.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FilterType.swift 3 | // Pyonsnal-Color 4 | // 5 | // Created by 조소정 on 2023/07/25. 6 | // 7 | 8 | import Foundation 9 | 10 | enum FilterType: Decodable { 11 | case sort // 정렬 12 | case recommend // 상품 추천 13 | case category // 카테고리 14 | case event // 행사 15 | case unknown 16 | 17 | init(from decoder: Decoder) throws { 18 | let container = try decoder.singleValueContainer() 19 | let type = try? container.decode(String.self) 20 | switch type { 21 | case "sort": self = .sort 22 | case "recommend": self = .recommend 23 | case "category": self = .category 24 | case "event": self = .event 25 | default: self = .unknown 26 | } 27 | } 28 | 29 | var filterDefaultText: String { 30 | switch self { 31 | case .sort: 32 | return "최신순" 33 | case .recommend: 34 | return "상품 추천" 35 | case .category: 36 | return "카테고리" 37 | case .event: 38 | return "행사" 39 | case .unknown: 40 | return "" 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Extension/URLRequest+.swift: -------------------------------------------------------------------------------- 1 | // 2 | // URLRequest+.swift 3 | // Pyonsnal-Color 4 | // 5 | // Created by 김진우 on 2023/06/06. 6 | // 7 | 8 | import Foundation 9 | 10 | extension URLRequest { 11 | var curlString: String { 12 | guard let url = url else { return "" } 13 | var baseCommand = "curl \(url.absoluteString)" 14 | 15 | if httpMethod == "HEAD" { 16 | baseCommand += " --head" 17 | } 18 | 19 | var command = [baseCommand] 20 | 21 | if let method = httpMethod, method != "GET" && method != "HEAD" { 22 | command.append("-X \(method)") 23 | } 24 | 25 | if let headers = allHTTPHeaderFields { 26 | for (key, value) in headers where key != "Cookie" { 27 | command.append("-H '\(key): \(value)'") 28 | } 29 | } 30 | 31 | if let data = httpBody, 32 | let body = String(data: data, encoding: .utf8) { 33 | command.append("-d '\(body)'") 34 | } 35 | 36 | return command.joined(separator: " \\\n\t") 37 | } 38 | 39 | init?(curlString: String) { 40 | return nil 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Extension/UIImageView+.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UIImageView+.swift 3 | // Pyonsnal-Color 4 | // 5 | // Created by 김진우 on 2023/06/06. 6 | // 7 | 8 | import UIKit 9 | import Kingfisher 10 | 11 | extension UIImageView { 12 | func setImage(_ kind: ImageAssetKind) { 13 | self.image = kind.image 14 | } 15 | 16 | func setImage(_ kind: ImageAssetKind.Icon) { 17 | self.image = kind.image 18 | } 19 | 20 | func setImage(_ kind: ImageAssetKind.StoreTag) { 21 | self.image = kind.image 22 | } 23 | 24 | func setImage(_ kind: ImageAssetKind.StoreIcon) { 25 | self.image = kind.image 26 | } 27 | 28 | func setImage(_ kind: ImageAssetKind.HeaderView) { 29 | self.image = kind.image 30 | } 31 | 32 | func setImage(_ kind: ImageAssetKind.Splash) { 33 | self.image = kind.image 34 | } 35 | 36 | func setImage(_ kind: ImageAssetKind.Profile) { 37 | self.image = kind.image 38 | } 39 | 40 | func setImage(with url: URL, completion: (() -> Void)? = nil) { 41 | self.kf.setImage(with: url) { _ in 42 | completion?() 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Networking/Config/Config.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Config.swift 3 | // Pyonsnal-Color 4 | // 5 | // Created by 조소정 on 2023/07/03. 6 | // 7 | 8 | import Foundation 9 | import Alamofire 10 | 11 | final class Config { 12 | 13 | static var shared = Config() 14 | 15 | private init() {} 16 | 17 | let baseURLString = "https://pyonsnalcolor.store" 18 | 19 | private let defaultHeaders: [HTTPHeader] = [ 20 | HTTPHeader(name: "Content-Type", value: "application/json"), 21 | HTTPHeader(name: "Accept", value: "*/*") 22 | ] 23 | } 24 | 25 | extension Config { 26 | 27 | func getDefaultHeader() -> [HTTPHeader]? { 28 | return defaultHeaders 29 | } 30 | 31 | func getAuthorizationHeader(with token: String) -> [HTTPHeader]? { 32 | return [HTTPHeader(name: "Authorization", value: token)] + defaultHeaders 33 | } 34 | 35 | func getMultipartFormDataHeader(id: String? = nil, token: String) -> [HTTPHeader]? { 36 | return [HTTPHeader(name: "Content-Type", value: "multipart/form-data; boundary=\(id ?? "")"), 37 | HTTPHeader(name: "Authorization", value: token)] 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Entity/Products/EventProductEntity.swift: -------------------------------------------------------------------------------- 1 | // 2 | // EventProductEntity.swift 3 | // Pyonsnal-Color 4 | // 5 | // Created by 김진우 on 2023/06/24. 6 | // 7 | 8 | import Foundation 9 | 10 | struct EventProductEntity: Decodable, ProductConvertable, Hashable { 11 | let productId: String 12 | let imageURL: URL 13 | let storeType: ConvenienceStore 14 | let updatedTime: String 15 | let name: String 16 | let price: String 17 | let originalPrice: String? 18 | let eventType: EventTag? 19 | let productType: ProductType 20 | let description: String? 21 | let isEventExpired: Bool? 22 | let giftItem: String? 23 | let isFavorite: Bool? 24 | let isNew: Bool? 25 | 26 | private enum CodingKeys: String, CodingKey { 27 | case productId = "id" 28 | case imageURL = "image" 29 | case storeType 30 | case updatedTime 31 | case name 32 | case price 33 | case originalPrice = "originPrice" 34 | case eventType 35 | case productType 36 | case description 37 | case isEventExpired 38 | case giftItem = "giftImage" 39 | case isFavorite 40 | case isNew 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Resource/Assets.xcassets/TabBar/icon_tab_my_selected.imageset/icon_tab_my_selected.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Common/Button/SortButton.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SortButton.swift 3 | // Pyonsnal-Color 4 | // 5 | // Created by 조소정 on 2023/07/22. 6 | // 7 | 8 | import UIKit 9 | 10 | final class SortButton: UIButton { 11 | 12 | enum Size { 13 | static let borderWidth: CGFloat = 1 14 | } 15 | 16 | // MARK: - Initializer 17 | init() { 18 | super.init(frame: .zero) 19 | configureUI() 20 | } 21 | 22 | required init?(coder: NSCoder) { 23 | fatalError("init(coder:) has not been implemented") 24 | } 25 | 26 | // MARK: - Private method 27 | private func configureUI() { 28 | self.isEnabled = false 29 | self.titleLabel?.font = .body3m 30 | self.backgroundColor = .white 31 | self.makeRounded(with: .spacing4) 32 | self.makeBorder( 33 | width: Size.borderWidth, 34 | color: UIColor.gray200.cgColor 35 | ) 36 | self.setImage(.sortFilter, for: .normal) 37 | self.setTitleColor(.gray500, for: .normal) 38 | self.contentEdgeInsets = UIEdgeInsets( 39 | top: 0, 40 | left: .spacing6, 41 | bottom: 0, 42 | right: .spacing10 43 | ) 44 | } 45 | 46 | } 47 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Resource/ImageAssetKind/ImageAssetKind.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ImageAssetKind.swift 3 | // Pyonsnal-Color 4 | // 5 | // Created by 김진우 on 2023/06/06. 6 | // 7 | 8 | import UIKit 9 | 10 | enum ImageAssetKind: String { 11 | case loginApple = "login_apple" 12 | case loginKakao = "login_kakao" 13 | 14 | case logo = "logo" 15 | case productTag = "product_tag" 16 | case eventTag = "event_tag" 17 | case keywordTag = "keyword_tag" 18 | 19 | case bellSimple = "bellSimple" 20 | case tagStore = "tag_store" 21 | 22 | case iconTitle = "icon_title" 23 | 24 | case event = "event" 25 | case eventSelected = "event.selected" 26 | 27 | case favorite = "icon_like_unselected" 28 | case favoriteSelected = "icon_like_selected" 29 | 30 | /// TermsOfUse 31 | case iconClose = "icon_close" 32 | case iconCloseSmall = "icon_close_small" 33 | case iconDetail = "icon_detail" 34 | case check = "check" 35 | case checkSelected = "check.selected" 36 | case checkSelectedRed = "check.selected.red" 37 | case iconArrow = "icon_arrow" 38 | } 39 | 40 | extension ImageAssetKind { 41 | var image: UIImage? { 42 | return UIImage(named: self.rawValue) 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/ProductHome/Views/ReusableView/CurationFooterView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CurationFooterView.swift 3 | // Pyonsnal-Color 4 | // 5 | // Created by 김인호 on 2023/07/24. 6 | // 7 | 8 | import UIKit 9 | import SnapKit 10 | 11 | final class CurationFooterView: UICollectionReusableView { 12 | 13 | enum Size { 14 | static let footerHeight: CGFloat = 12 15 | } 16 | 17 | private let dividerView: UIView = { 18 | let view = UIView() 19 | view.backgroundColor = .gray100 20 | return view 21 | }() 22 | 23 | override init(frame: CGRect) { 24 | super.init(frame: .zero) 25 | 26 | configureLayout() 27 | } 28 | 29 | required init?(coder: NSCoder) { 30 | fatalError("init(coder:) has not been implemented") 31 | } 32 | 33 | private func configureLayout() { 34 | addSubview(dividerView) 35 | 36 | dividerView.snp.makeConstraints { make in 37 | make.leading.trailing.bottom.equalToSuperview() 38 | make.height.equalTo(Size.footerHeight) 39 | } 40 | } 41 | 42 | override func prepareForReuse() { 43 | super.prepareForReuse() 44 | 45 | isHidden = false 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Common/Combine/ControlSubscription.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ControlSubscription.swift 3 | // Pyonsnal-Color 4 | // 5 | // Created by 조소정 on 2023/09/06. 6 | // 7 | 8 | import UIKit 9 | import Combine 10 | 11 | final class ControlSubscription: Subscription where ControlSubscriber.Input == ControlPublisher.Output, ControlSubscriber.Failure == ControlPublisher.Failure { 12 | private let selector = #selector(eventHandler) 13 | private var subscriber: ControlSubscriber? 14 | private let control: UIControl 15 | private let controlEvent: UIControl.Event 16 | 17 | init(subscriber: ControlSubscriber, control: UIControl, controlEvent: UIControl.Event) { 18 | self.subscriber = subscriber 19 | self.control = control 20 | self.controlEvent = controlEvent 21 | control.addTarget(self, action: selector, for: controlEvent) 22 | } 23 | 24 | func request(_ demand: Subscribers.Demand) { } // 구현 필요 없음 25 | 26 | func cancel() { 27 | subscriber = nil 28 | control.removeTarget(self, action: selector, for: controlEvent) 29 | } 30 | 31 | @objc 32 | func eventHandler() { 33 | _ = subscriber?.receive(control) 34 | } 35 | } 36 | 37 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Common/Button/SelectionButton.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SelectionButton.swift 3 | // Pyonsnal-Color 4 | // 5 | // Created by 김진우 on 2023/07/30. 6 | // 7 | 8 | import UIKit 9 | 10 | final class SelectionButton: UIButton { 11 | 12 | // MARK: - Declaration 13 | 14 | // MARK: - Initializer 15 | init() { 16 | super.init(frame: .zero) 17 | 18 | configureUI() 19 | } 20 | 21 | required init?(coder: NSCoder) { 22 | fatalError("init(coder:) has not been implemented") 23 | } 24 | 25 | // MARK: - Override Method 26 | // override func layoutSubviews() { 27 | // if imageView != nil { 28 | // imageEdgeInsets = .init(top: 0, left: (bounds.width - 35), bottom: 0, right: 5) 29 | // } 30 | // } 31 | 32 | // MARK: - Private Method 33 | private func configureUI() { 34 | contentHorizontalAlignment = .left 35 | semanticContentAttribute = .forceRightToLeft 36 | titleLabel?.font = .body2m 37 | setTitleColor(.red500, for: .selected) 38 | setTitleColor(.gray700, for: .normal) 39 | setImage(ImageAssetKind.Icon.iconCheck.image?.withTintColor(.red500), for: .selected) 40 | setImage(.none, for: .normal) 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/NotificationList/NotificationListInteractor.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NotificationListInteractor.swift 3 | // Pyonsnal-Color 4 | // 5 | // Created by 김인호 on 2023/06/27. 6 | // 7 | 8 | import ModernRIBs 9 | 10 | protocol NotificationListRouting: ViewableRouting { 11 | } 12 | 13 | protocol NotificationListPresentable: Presentable { 14 | var listener: NotificationListPresentableListener? { get set } 15 | } 16 | 17 | protocol NotificationListListener: AnyObject { 18 | func notificationListDidTapBackButton() 19 | } 20 | 21 | final class NotificationListInteractor: PresentableInteractor, NotificationListInteractable, NotificationListPresentableListener { 22 | 23 | weak var router: NotificationListRouting? 24 | weak var listener: NotificationListListener? 25 | 26 | override init(presenter: NotificationListPresentable) { 27 | super.init(presenter: presenter) 28 | presenter.listener = self 29 | } 30 | 31 | override func didBecomeActive() { 32 | super.didBecomeActive() 33 | } 34 | 35 | override func willResignActive() { 36 | super.willResignActive() 37 | } 38 | 39 | func didTabBackButton() { 40 | listener?.notificationListDidTapBackButton() 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Extension/UICollectionView+.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UICollectionView+.swift 3 | // Pyonsnal-Color 4 | // 5 | // Created by 김인호 on 2023/06/23. 6 | // 7 | 8 | import UIKit 9 | 10 | extension UICollectionView { 11 | func register(_ viewClass: AnyClass) { 12 | register(viewClass, forCellWithReuseIdentifier: String(describing: viewClass.self)) 13 | } 14 | 15 | func registerHeaderView(_ viewClass: AnyClass) { 16 | register( 17 | viewClass, 18 | forSupplementaryViewOfKind: UICollectionView.elementKindSectionHeader, 19 | withReuseIdentifier: String(describing: viewClass.self) 20 | ) 21 | } 22 | 23 | func registerFooterView(_ viewClass: AnyClass) { 24 | register( 25 | viewClass, 26 | forSupplementaryViewOfKind: UICollectionView.elementKindSectionFooter, 27 | withReuseIdentifier: String(describing: viewClass.self) 28 | ) 29 | } 30 | 31 | func dequeueReusableCell(for indexPath: IndexPath) -> T { 32 | guard let cell = self.dequeueReusableCell(withReuseIdentifier: T.identifier, for: indexPath) as? T else { 33 | fatalError("Unexpected identifier : \(T.identifier)") 34 | } 35 | return cell 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/TermsOfUse/TermsOfUseBuilder.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TermsOfUseBuilder.swift 3 | // Pyonsnal-Color 4 | // 5 | // Created by 조소정 on 2023/07/06. 6 | // 7 | 8 | import ModernRIBs 9 | 10 | protocol TermsOfUseDependency: Dependency { 11 | 12 | } 13 | 14 | final class TermsOfUseComponent: Component, 15 | CommonWebDependency { 16 | 17 | } 18 | 19 | // MARK: - Builder 20 | 21 | protocol TermsOfUseBuildable: Buildable { 22 | func build(withListener listener: TermsOfUseListener) -> TermsOfUseRouting 23 | } 24 | 25 | final class TermsOfUseBuilder: Builder, TermsOfUseBuildable { 26 | 27 | 28 | override init(dependency: TermsOfUseDependency) { 29 | super.init(dependency: dependency) 30 | } 31 | 32 | func build(withListener listener: TermsOfUseListener) -> TermsOfUseRouting { 33 | let component = TermsOfUseComponent(dependency: dependency) 34 | let viewController = TermsOfUseViewController() 35 | let commonWebBuilder = CommonWebBuilder(dependency: component) 36 | let interactor = TermsOfUseInteractor(presenter: viewController) 37 | interactor.listener = listener 38 | return TermsOfUseRouter(interactor: interactor, viewController: viewController, commonWebBuilder: commonWebBuilder) 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/LoginPopup/LoginPopupBuilder.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LoginPopupBuilder.swift 3 | // Pyonsnal-Color 4 | // 5 | // Created by 김인호 on 1/31/24. 6 | // 7 | 8 | import ModernRIBs 9 | 10 | protocol LoginPopupDependency: Dependency { 11 | // TODO: Declare the set of dependencies required by this RIB, but cannot be 12 | // created by this RIB. 13 | } 14 | 15 | final class LoginPopupComponent: Component { 16 | 17 | // TODO: Declare 'fileprivate' dependencies that are only used by this RIB. 18 | } 19 | 20 | // MARK: - Builder 21 | 22 | protocol LoginPopupBuildable: Buildable { 23 | func build(withListener listener: LoginPopupListener) -> LoginPopupRouting 24 | } 25 | 26 | final class LoginPopupBuilder: Builder, LoginPopupBuildable { 27 | 28 | override init(dependency: LoginPopupDependency) { 29 | super.init(dependency: dependency) 30 | } 31 | 32 | func build(withListener listener: LoginPopupListener) -> LoginPopupRouting { 33 | let component = LoginPopupComponent(dependency: dependency) 34 | let viewController = LoginPopupViewController() 35 | let interactor = LoginPopupInteractor(presenter: viewController) 36 | interactor.listener = listener 37 | return LoginPopupRouter(interactor: interactor, viewController: viewController) 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/LoginPopup/LoginPopupInteractor.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LoginPopupInteractor.swift 3 | // Pyonsnal-Color 4 | // 5 | // Created by 김인호 on 1/31/24. 6 | // 7 | 8 | import ModernRIBs 9 | 10 | protocol LoginPopupRouting: ViewableRouting { 11 | } 12 | 13 | protocol LoginPopupPresentable: Presentable { 14 | var listener: LoginPopupPresentableListener? { get set } 15 | } 16 | 17 | protocol LoginPopupListener: AnyObject { 18 | func popupDidTapDismiss() 19 | func popupDidTapConfirm() 20 | } 21 | 22 | final class LoginPopupInteractor: PresentableInteractor, 23 | LoginPopupInteractable, 24 | LoginPopupPresentableListener { 25 | 26 | weak var router: LoginPopupRouting? 27 | weak var listener: LoginPopupListener? 28 | 29 | override init(presenter: LoginPopupPresentable) { 30 | super.init(presenter: presenter) 31 | presenter.listener = self 32 | } 33 | 34 | override func didBecomeActive() { 35 | super.didBecomeActive() 36 | } 37 | 38 | override func willResignActive() { 39 | super.willResignActive() 40 | } 41 | 42 | func popupDidTapDismiss() { 43 | listener?.popupDidTapDismiss() 44 | } 45 | 46 | func popupDidTapConfirm() { 47 | listener?.popupDidTapConfirm() 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Service/UserAuthService.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UserAuthService.swift 3 | // Pyonsnal-Color 4 | // 5 | // Created by 김진우 on 2023/07/08. 6 | // 7 | 8 | final class UserAuthService { 9 | 10 | // MARK: - Private Property 11 | private let accessTokenKey: String = "UserAuthService.AccessToken.Key" 12 | private let refreshTokenKey: String = "UserAuthService.RefreshToken.Key" 13 | private let keyChainService: KeyChainService 14 | 15 | // MARK: - Initializer 16 | init(keyChainService: KeyChainService) { 17 | self.keyChainService = keyChainService 18 | } 19 | 20 | // MARK: - Interface 21 | func getAccessToken() -> String? { 22 | keyChainService.get(with: accessTokenKey) 23 | } 24 | 25 | func getRefreshToken() -> String? { 26 | keyChainService.get(with: refreshTokenKey) 27 | } 28 | 29 | func setAccessToken(_ token: String) { 30 | _ = keyChainService.set(value: token, to: accessTokenKey) 31 | } 32 | 33 | func setRefreshToken(_ token: String) { 34 | _ = keyChainService.set(value: token, to: refreshTokenKey) 35 | } 36 | 37 | func deleteAccessToken() { 38 | keyChainService.delete(with: accessTokenKey) 39 | } 40 | 41 | func deleteRefreshToken() { 42 | keyChainService.delete(with: refreshTokenKey) 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/RootTabBar/RootTabBarInteractor.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RootTabBarInteractor.swift 3 | // Pyonsnal-Color 4 | // 5 | // Created by 김인호 on 2023/06/10. 6 | // 7 | 8 | import ModernRIBs 9 | 10 | protocol RootTabBarRouting: ViewableRouting { 11 | func attachTabs() 12 | } 13 | 14 | protocol RootTabBarPresentable: Presentable { 15 | var listener: RootTabBarPresentableListener? { get set } 16 | } 17 | 18 | protocol RootTabBarListener: AnyObject { 19 | func routeToLoggedOut() 20 | func routeToLoggedIn() 21 | } 22 | 23 | final class RootTabBarInteractor: 24 | PresentableInteractor, 25 | RootTabBarInteractable, 26 | RootTabBarPresentableListener { 27 | 28 | weak var router: RootTabBarRouting? 29 | weak var listener: RootTabBarListener? 30 | 31 | override init(presenter: RootTabBarPresentable) { 32 | super.init(presenter: presenter) 33 | presenter.listener = self 34 | } 35 | 36 | override func didBecomeActive() { 37 | super.didBecomeActive() 38 | 39 | router?.attachTabs() 40 | } 41 | 42 | override func willResignActive() { 43 | super.willResignActive() 44 | } 45 | 46 | func routeToLoggedOut() { 47 | listener?.routeToLoggedOut() 48 | } 49 | 50 | func routeToLoggedIn() { 51 | listener?.routeToLoggedIn() 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Extension/UIView+.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UIView+.swift 3 | // Pyonsnal-Color 4 | // 5 | // Created by 조소정 on 2023/06/14. 6 | // 7 | 8 | import UIKit 9 | 10 | extension UIView { 11 | /// 둥글게 만들어줍니다. 12 | /// - Parameter cornerRadius: 곡선 값 13 | func makeRounded(with cornerRadius: CGFloat) { 14 | self.layer.cornerRadius = cornerRadius 15 | self.layer.masksToBounds = true 16 | } 17 | 18 | /// 특정 모서리만 둥글게 만들어 줍니다. 19 | /// - Parameter cornerRadius: 곡선 값 20 | /// - Parameter maskedCorners: 적용할 corners 21 | func makeRoundCorners(cornerRadius: CGFloat, maskedCorners: CACornerMask) { 22 | self.clipsToBounds = true 23 | self.layer.cornerRadius = cornerRadius 24 | self.layer.maskedCorners = CACornerMask(arrayLiteral: maskedCorners) 25 | } 26 | 27 | func makeBorder(width: CGFloat, color: CGColor) { 28 | self.layer.borderWidth = width 29 | self.layer.borderColor = color 30 | } 31 | 32 | func removeBorder() { 33 | self.layer.borderWidth = 0 34 | } 35 | 36 | func addTapGesture(tapNumber: Int = 1, target: AnyObject, action: Selector) { 37 | let tap = UITapGestureRecognizer(target: target, action: action) 38 | tap.numberOfTapsRequired = tapNumber 39 | addGestureRecognizer(tap) 40 | isUserInteractionEnabled = true 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/ProductHome/Views/Cell/CurationImageCell.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CurationImageCell.swift 3 | // Pyonsnal-Color 4 | // 5 | // Created by 김인호 on 2023/07/24. 6 | // 7 | 8 | import UIKit 9 | import SnapKit 10 | import Lottie 11 | 12 | final class CurationImageCell: UICollectionViewCell { 13 | 14 | let containerView: UIView = { 15 | let view = UIView() 16 | return view 17 | }() 18 | 19 | private let introductionImageView: LottieAnimationView = { 20 | let animation = LottieAnimation.named("main") 21 | let animationView = LottieAnimationView(animation: animation) 22 | animationView.contentMode = .scaleAspectFill 23 | animationView.loopMode = .loop 24 | animationView.play() 25 | return animationView 26 | }() 27 | 28 | override init(frame: CGRect) { 29 | super.init(frame: frame) 30 | 31 | configureLayout() 32 | } 33 | 34 | required init?(coder: NSCoder) { 35 | fatalError("init(coder:) has not been implemented") 36 | } 37 | 38 | private func configureLayout() { 39 | addSubview(containerView) 40 | containerView.addSubview(introductionImageView) 41 | 42 | containerView.snp.makeConstraints { 43 | $0.edges.equalToSuperview() 44 | } 45 | introductionImageView.frame = self.bounds 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Root/RootComponent.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RootComponent.swift 3 | // Pyonsnal-Color 4 | // 5 | // Created by 김인호 on 2023/06/10. 6 | // 7 | 8 | import ModernRIBs 9 | 10 | protocol RootDependencyLoggedOut: Dependency { 11 | } 12 | 13 | final class RootComponent: Component { 14 | 15 | let rootViewController: RootViewController 16 | var appleLoginService: AppleLoginService 17 | var kakaoLoginService: KakaoLoginService 18 | let userAuthService: UserAuthService 19 | var authClient: AuthAPIService 20 | 21 | init(rootViewController: RootViewController, 22 | dependency: RootDependency 23 | ) { 24 | self.rootViewController = rootViewController 25 | self.appleLoginService = AppleLoginService() 26 | self.kakaoLoginService = KakaoLoginService() 27 | let keyChainService = KeyChainService.shared 28 | self.userAuthService = .init(keyChainService: keyChainService) 29 | let pyonsnalColorClient = PyonsnalColorClient() 30 | self.authClient = .init(client: pyonsnalColorClient) 31 | super.init(dependency: dependency) 32 | } 33 | } 34 | 35 | extension RootComponent: LoggedOutDependency { 36 | } 37 | 38 | extension RootComponent: LoggedInDependency { 39 | 40 | var loggedInViewController: LoggedInViewControllable { 41 | return rootViewController 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Root/RootBuilder.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RootBuilder.swift 3 | // Pyonsnal-Color 4 | // 5 | // Created by 김진우 on 2023/05/29. 6 | // 7 | 8 | import ModernRIBs 9 | 10 | protocol RootDependency: Dependency { 11 | var userAuthService: UserAuthService { get } 12 | } 13 | 14 | // MARK: - Builder 15 | 16 | protocol RootBuildable: Buildable { 17 | func build() -> LaunchRouting 18 | } 19 | 20 | final class RootBuilder: Builder, RootBuildable { 21 | 22 | override init(dependency: RootDependency) { 23 | super.init(dependency: dependency) 24 | } 25 | 26 | func build() -> LaunchRouting { 27 | let viewController = RootViewController() 28 | let component: RootComponent = .init( 29 | rootViewController: viewController, 30 | dependency: dependency 31 | ) 32 | let interactor: RootInteractor = .init(presenter: viewController, dependency: dependency) 33 | let loggedOutBuilder: LoggedOutBuilder = .init(dependency: component) 34 | let loggedInBuilder: LoggedInBuilder = .init(dependency: component) 35 | let router: RootRouter = .init( 36 | interactor: interactor, 37 | viewController: viewController, 38 | loggedOutBuilder: loggedOutBuilder, 39 | loggedInBuilder: loggedInBuilder 40 | ) 41 | 42 | return router 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/ProductDetail/Subview/ImageCell/ProductDetailImageCell.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ProductDetailImageCell.swift 3 | // Pyonsnal-Color 4 | // 5 | // Created by 김진우 on 2023/09/10. 6 | // 7 | 8 | import UIKit 9 | 10 | final class ProductDetailImageCell: UICollectionViewCell { 11 | 12 | // MARK: - Declaration 13 | struct Payload { 14 | let imageURL: URL 15 | } 16 | 17 | // MARK: - Interface 18 | var payload: Payload? { 19 | didSet { configure() } 20 | } 21 | 22 | // MARK: - Private Property 23 | private let viewHolder: ViewHolder = .init() 24 | 25 | // MARK: - Initializer 26 | override init(frame: CGRect) { 27 | super.init(frame: .zero) 28 | 29 | configureView() 30 | configureConstraint() 31 | } 32 | 33 | required init?(coder: NSCoder) { 34 | fatalError("init(coder:) has not been implemented") 35 | } 36 | 37 | // MARK: - Private Method 38 | private func configureView() { 39 | viewHolder.place(in: contentView) 40 | } 41 | 42 | private func configureConstraint() { 43 | viewHolder.configureConstraints(for: contentView) 44 | } 45 | 46 | private func configure() { 47 | guard let payload else { 48 | return 49 | } 50 | viewHolder.imageView.setImage(with: payload.imageURL) 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Service/KakaoLoginService.swift: -------------------------------------------------------------------------------- 1 | // 2 | // KakaoLoginService.swift 3 | // Pyonsnal-Color 4 | // 5 | // Created by 김인호 on 2023/06/06. 6 | // 7 | 8 | import Foundation 9 | import KakaoSDKUser 10 | 11 | protocol KakaoLoginServiceDelegate: AnyObject { 12 | func didReceive(accessToken: String) 13 | } 14 | 15 | final class KakaoLoginService: NSObject { 16 | 17 | weak var delegate: KakaoLoginServiceDelegate? 18 | 19 | func requestKakaoLogin() { 20 | if UserApi.isKakaoTalkLoginAvailable() { 21 | UserApi.shared.loginWithKakaoTalk { [weak self] (oauthToken, error) in 22 | if let error = error { 23 | print(error) 24 | } else { 25 | if let accessToken = oauthToken?.accessToken { 26 | self?.delegate?.didReceive(accessToken: accessToken) 27 | } 28 | } 29 | } 30 | } else { 31 | UserApi.shared.loginWithKakaoAccount { [weak self] (oauthToken, error) in 32 | if let error = error { 33 | print(error) 34 | } else { 35 | if let accessToken = oauthToken?.accessToken { 36 | self?.delegate?.didReceive(accessToken: accessToken) 37 | } 38 | } 39 | } 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Networking/Auth/AuthAPIService.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AuthAPIService.swift 3 | // Pyonsnal-Color 4 | // 5 | // Created by 김진우 on 2023/07/08. 6 | // 7 | 8 | final class AuthAPIService { 9 | 10 | // MARK: - Private Property 11 | private let client: PyonsnalColorClient 12 | 13 | // MARK: - Initializer 14 | init(client: PyonsnalColorClient) { 15 | self.client = client 16 | } 17 | 18 | // MARK: - Interface 19 | func login(token: String, authType: AuthType) -> ResponsePublisher { 20 | return client.request( 21 | AuthRouter.login(token: token, authType: authType), 22 | model: UserAuthEntity.self 23 | ) 24 | } 25 | 26 | func guestLogin() -> ResponsePublisher { 27 | return client.request(AuthRouter.guestLogin, model: UserAuthEntity.self) 28 | } 29 | 30 | func loginStatus(token: String, authType: AuthType) -> ResponsePublisher { 31 | return client.request( 32 | AuthRouter.loginStatus(token: token, authType: authType), 33 | model: UserLoginStatusEntity.self 34 | ) 35 | } 36 | 37 | func reissue(userAuth: UserAuthEntity) -> ResponsePublisher { 38 | return client.request( 39 | AuthRouter.reissue(userAuth: userAuth), 40 | model: UserAuthEntity.self 41 | ) 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Resource/Assets.xcassets/TabBar/icon_tab_sale_unselected.imageset/icon_tab_sale_unselected.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/NotificationList/NotificationListBuilder.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NotificationListBuilder.swift 3 | // Pyonsnal-Color 4 | // 5 | // Created by 김인호 on 2023/06/27. 6 | // 7 | 8 | import ModernRIBs 9 | 10 | protocol NotificationListDependency: Dependency { 11 | // TODO: Declare the set of dependencies required by this RIB, but cannot be 12 | // created by this RIB. 13 | } 14 | 15 | final class NotificationListComponent: Component { 16 | 17 | // TODO: Declare 'fileprivate' dependencies that are only used by this RIB. 18 | } 19 | 20 | // MARK: - Builder 21 | 22 | protocol NotificationListBuildable: Buildable { 23 | func build(withListener listener: NotificationListListener) -> NotificationListRouting 24 | } 25 | 26 | final class NotificationListBuilder: Builder, NotificationListBuildable { 27 | 28 | override init(dependency: NotificationListDependency) { 29 | super.init(dependency: dependency) 30 | } 31 | 32 | func build(withListener listener: NotificationListListener) -> NotificationListRouting { 33 | let component = NotificationListComponent(dependency: dependency) 34 | let viewController = NotificationListViewController() 35 | let interactor = NotificationListInteractor(presenter: viewController) 36 | interactor.listener = listener 37 | return NotificationListRouter(interactor: interactor, viewController: viewController) 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Common/Combine/GestureSubscription.swift: -------------------------------------------------------------------------------- 1 | // 2 | // GestureSubscription.swift 3 | // Pyonsnal-Color 4 | // 5 | // Created by 조소정 on 2023/09/06. 6 | // 7 | 8 | import UIKit 9 | import Combine 10 | 11 | final class GestureSubscription: Subscription where GestureSubscriber.Input == GesturePublisher.Output, GestureSubscriber.Failure == GesturePublisher.Failure { 12 | private let selector = #selector(gestureHandler) 13 | private var subscriber: GestureSubscriber? 14 | private weak var targetView: UIView? 15 | private let gestureRecognizer: UIGestureRecognizer 16 | 17 | init(subscriber: GestureSubscriber?, view: UIView?, gestureRecognizer: UIGestureRecognizer) { 18 | self.subscriber = subscriber 19 | self.targetView = view 20 | self.gestureRecognizer = gestureRecognizer 21 | 22 | self.targetView?.isUserInteractionEnabled = true 23 | gestureRecognizer.addTarget(self, action: selector) 24 | self.targetView?.addGestureRecognizer(gestureRecognizer) 25 | } 26 | 27 | func request(_ demand: Subscribers.Demand) { } // 구현 필요 없음 28 | 29 | func cancel() { 30 | subscriber = nil 31 | targetView?.isUserInteractionEnabled = false 32 | gestureRecognizer.removeTarget(self, action: selector) 33 | } 34 | 35 | @objc 36 | private func gestureHandler() { 37 | _ = subscriber?.receive(gestureRecognizer) 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/LoggedIn/LoggedInBuilder.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LoggedInBuilder.swift 3 | // Pyonsnal-Color 4 | // 5 | // Created by 김인호 on 2023/06/10. 6 | // 7 | 8 | import ModernRIBs 9 | 10 | protocol LoggedInDependency: Dependency { 11 | var loggedInViewController: LoggedInViewControllable { get } 12 | } 13 | 14 | final class LoggedInComponent: Component, RootTabBarDependency { 15 | fileprivate var loggedInViewController: LoggedInViewControllable { 16 | return dependency.loggedInViewController 17 | } 18 | } 19 | 20 | // MARK: - Builder 21 | 22 | protocol LoggedInBuildable: Buildable { 23 | func build(withListener listener: LoggedInListener) -> LoggedInRouting 24 | } 25 | 26 | final class LoggedInBuilder: Builder, LoggedInBuildable { 27 | 28 | override init(dependency: LoggedInDependency) { 29 | super.init(dependency: dependency) 30 | } 31 | 32 | func build(withListener listener: LoggedInListener) -> LoggedInRouting { 33 | let component = LoggedInComponent(dependency: dependency) 34 | let interactor = LoggedInInteractor() 35 | let rootTabBarBuilder = RootTabBarBuilder(dependency: component) 36 | interactor.listener = listener 37 | let router = LoggedInRouter( 38 | interactor: interactor, 39 | viewController: component.loggedInViewController, 40 | rootTabBarBuilder: rootTabBarBuilder 41 | ) 42 | 43 | return router 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/TermsOfUse/View/TermsButton.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TermsButton.swift 3 | // Pyonsnal-Color 4 | // 5 | // Created by 조소정 on 2023/07/06. 6 | // 7 | 8 | import UIKit 9 | 10 | final class TermsButton: UIButton { 11 | 12 | // MARK: - Initializer 13 | init( 14 | textColor: UIColor, 15 | font: UIFont, 16 | isSelected: Bool 17 | ) { 18 | super.init(frame: .zero) 19 | configureUI(textColor: textColor, 20 | font: font) 21 | } 22 | 23 | required init?(coder: NSCoder) { 24 | fatalError("init(coder:) has not been implemented") 25 | } 26 | 27 | // MARK: - Method 28 | func setButtonState(isSelected: Bool) { 29 | self.isSelected = isSelected 30 | } 31 | 32 | func isCurrentSelected() -> Bool { 33 | return isSelected 34 | } 35 | 36 | // MARK: - Private method 37 | private func configureUI(textColor: UIColor, 38 | font: UIFont) { 39 | self.titleLabel?.font = font 40 | self.setTitleColor(textColor, for: .normal) 41 | self.setImage(.check, for: .normal) 42 | self.setImage(.checkSelected, for: .selected) 43 | self.contentHorizontalAlignment = .left 44 | self.titleEdgeInsets = UIEdgeInsets(top: 0, 45 | left: .spacing8, 46 | bottom: 0, 47 | right: 0) 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/LoginPopup/LoginPopupViewHolder.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LoginPopupViewHolder.swift 3 | // Pyonsnal-Color 4 | // 5 | // Created by 김인호 on 1/31/24. 6 | // 7 | 8 | import UIKit 9 | import SnapKit 10 | 11 | extension LoginPopupViewController { 12 | final class ViewHolder: ViewHolderable { 13 | 14 | enum Constant { 15 | static let popupHeight: CGFloat = 208 16 | 17 | static let backgroundColor: UIColor = .black.withAlphaComponent(0.4) 18 | } 19 | 20 | private let backgroundView: UIView = { 21 | let view = UIView() 22 | view.backgroundColor = Constant.backgroundColor 23 | return view 24 | }() 25 | 26 | let popupView = PopupView(state: .normal) 27 | 28 | func place(in view: UIView) { 29 | view.addSubview(backgroundView) 30 | 31 | backgroundView.addSubview(popupView) 32 | } 33 | 34 | func configureConstraints(for view: UIView) { 35 | backgroundView.snp.makeConstraints { 36 | $0.edges.equalTo(view) 37 | } 38 | 39 | popupView.snp.makeConstraints { 40 | $0.leading.equalToSuperview().offset(.spacing16) 41 | $0.trailing.equalToSuperview().inset(.spacing16) 42 | $0.centerY.equalToSuperview() 43 | $0.height.equalTo(Constant.popupHeight) 44 | } 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Entity/Filter/FilterEntity.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FilterEntity.swift 3 | // Pyonsnal-Color 4 | // 5 | // Created by 조소정 on 2023/07/22. 6 | // 7 | 8 | import UIKit 9 | 10 | struct FilterDataEntity: Decodable, Hashable { 11 | var data: [FilterEntity] 12 | } 13 | 14 | struct FilterEntity: Decodable { 15 | let filterType: FilterType 16 | var defaultText: String? // 카테고리에 보여질 이름 17 | var isSelected: Bool = false 18 | var filterItem: [FilterItemEntity] 19 | 20 | enum CodingKeys: String, CodingKey { 21 | case filterType, filterItem 22 | } 23 | 24 | init(filterType: FilterType, defaultText: String?, filterItem: [FilterItemEntity]) { 25 | self.filterType = filterType 26 | self.defaultText = defaultText 27 | self.filterItem = filterItem 28 | } 29 | } 30 | 31 | extension FilterEntity: Hashable { 32 | static func == (lhs: FilterEntity, rhs: FilterEntity) -> Bool { 33 | return lhs.filterItem == rhs.filterItem 34 | } 35 | 36 | func hash(into hasher: inout Hasher) { 37 | hasher.combine(filterItem) 38 | } 39 | } 40 | 41 | struct FilterItemEntity: Decodable, Hashable { 42 | let name: String 43 | let code: Int 44 | let image: String? 45 | 46 | var isSelected = false 47 | 48 | enum CodingKeys: String, CodingKey { 49 | case name, code, image 50 | } 51 | 52 | func hash(into hasher: inout Hasher) { 53 | hasher.combine(name) 54 | hasher.combine(code) 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Service/UserInfoService.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UserInfoService.swift 3 | // Pyonsnal-Color 4 | // 5 | // Created by 김진우 on 2023/10/25. 6 | // 7 | 8 | import Combine 9 | import AdSupport 10 | 11 | final class UserInfoService { 12 | 13 | static let shared = UserInfoService() 14 | 15 | // MARK: - Private Property 16 | 17 | private var memberInfo: MemberInfoEntity? 18 | private var cancellable = Set() 19 | 20 | private let client = PyonsnalColorClient() 21 | private let userAuthService = UserAuthService(keyChainService: .shared) 22 | private lazy var memberAPIService = MemberAPIService(client: client, userAuthService: userAuthService) 23 | 24 | var memberID: Int? { 25 | return memberInfo?.memberId 26 | } 27 | var adId: String { 28 | ASIdentifierManager.shared().advertisingIdentifier.uuidString 29 | } 30 | var isGuest: Bool? { 31 | return memberInfo?.isGuest 32 | } 33 | 34 | // MARK: - Initializer 35 | 36 | private init() {} 37 | 38 | // MARK: - Private Method 39 | 40 | func configure() { 41 | requestMemberInfo() 42 | } 43 | 44 | private func requestMemberInfo() { 45 | memberAPIService.info() 46 | .sink { [weak self] response in 47 | if let memberInfo = response.value { 48 | self?.memberInfo = memberInfo 49 | } 50 | } 51 | .store(in: &cancellable) 52 | } 53 | 54 | } 55 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/ReviewPopup/ReviewPopupInteractor.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ReviewPopupInteractor.swift 3 | // Pyonsnal-Color 4 | // 5 | // Created by 김인호 on 2023/09/21. 6 | // 7 | 8 | import ModernRIBs 9 | 10 | protocol ReviewPopupRouting: ViewableRouting { 11 | } 12 | 13 | protocol ReviewPopupPresentable: Presentable { 14 | var listener: ReviewPopupPresentableListener? { get set } 15 | } 16 | 17 | protocol ReviewPopupListener: AnyObject { 18 | func popupDidTapDismissButton() 19 | func popupDidTapBackButton() 20 | func routeToProductDetail() 21 | } 22 | 23 | final class ReviewPopupInteractor: PresentableInteractor, 24 | ReviewPopupInteractable, 25 | ReviewPopupPresentableListener { 26 | 27 | weak var router: ReviewPopupRouting? 28 | weak var listener: ReviewPopupListener? 29 | 30 | override init(presenter: ReviewPopupPresentable) { 31 | super.init(presenter: presenter) 32 | presenter.listener = self 33 | } 34 | 35 | override func didBecomeActive() { 36 | super.didBecomeActive() 37 | } 38 | 39 | override func willResignActive() { 40 | super.willResignActive() 41 | } 42 | 43 | func didTapApplyButton() { 44 | listener?.routeToProductDetail() 45 | } 46 | 47 | func didTapDismissButton() { 48 | listener?.popupDidTapDismissButton() 49 | } 50 | 51 | func didTapBackButton() { 52 | listener?.popupDidTapBackButton() 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Entity/Curation/HomeBannerEntity.swift: -------------------------------------------------------------------------------- 1 | // 2 | // HomeBannerEntity.swift 3 | // Pyonsnal-Color 4 | // 5 | // Created by 김인호 on 12/26/23. 6 | // 7 | 8 | import Foundation 9 | 10 | struct HomeBanner: Decodable { 11 | let homeBanner: [HomeBannerEntity] 12 | } 13 | 14 | struct HomeBannerEntity: Decodable { 15 | let type: String 16 | let value: [HomeBannerValue] 17 | } 18 | 19 | struct HomeBannerValue: Decodable { 20 | let curationProducts: CurationEntity? 21 | let eventBanners: EventImageEntity? 22 | 23 | enum CodingKeys: CodingKey { 24 | case curationProducts 25 | case eventBanners 26 | } 27 | 28 | init(curationProducts: CurationEntity? = nil, eventBanners: EventImageEntity? = nil) { 29 | self.curationProducts = curationProducts 30 | self.eventBanners = eventBanners 31 | } 32 | 33 | init(from decoder: Decoder) throws { 34 | let container = try decoder.singleValueContainer() 35 | 36 | if let value = try? container.decode(CurationEntity.self) { 37 | self = .init(curationProducts: value) 38 | return 39 | } 40 | 41 | if let value = try? container.decode(EventImageEntity.self) { 42 | self = .init(eventBanners: value) 43 | return 44 | } 45 | 46 | throw DecodingError.typeMismatch( 47 | HomeBannerValue.self, 48 | .init(codingPath: decoder.codingPath, debugDescription: "Type Mismatch") 49 | ) 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Resource/Assets.xcassets/Review/star_filled_large.imageset/filled=filled, size=L.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Resource/Assets.xcassets/Review/star_filled_medium.imageset/filled=filled, size=m.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color.xcodeproj/xcuserdata/gamza.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | Promises (Playground) 1.xcscheme 8 | 9 | isShown 10 | 11 | orderHint 12 | 2 13 | 14 | Promises (Playground) 2.xcscheme 15 | 16 | isShown 17 | 18 | orderHint 19 | 3 20 | 21 | Promises (Playground).xcscheme 22 | 23 | isShown 24 | 25 | orderHint 26 | 0 27 | 28 | Pyonsnal-Color.xcscheme_^#shared#^_ 29 | 30 | orderHint 31 | 0 32 | 33 | SnapKitPlayground (Playground) 1.xcscheme 34 | 35 | isShown 36 | 37 | orderHint 38 | 2 39 | 40 | SnapKitPlayground (Playground) 2.xcscheme 41 | 42 | isShown 43 | 44 | orderHint 45 | 3 46 | 47 | SnapKitPlayground (Playground).xcscheme 48 | 49 | isShown 50 | 51 | orderHint 52 | 0 53 | 54 | 55 | 56 | 57 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Common/Cell/EmptyCell.swift: -------------------------------------------------------------------------------- 1 | // 2 | // EmptyCell.swift 3 | // Pyonsnal-Color 4 | // 5 | // Created by 김진우 on 2023/07/24. 6 | // 7 | 8 | import UIKit 9 | import SnapKit 10 | 11 | final class EmptyCell: UICollectionViewCell { 12 | 13 | // MARK: - Declaration 14 | enum Text { 15 | enum Search { 16 | static let titleLabelText = "앗! 원하는 검색 결과가 없어요." 17 | static let subtitleLabelText = "단어의 철자가 올바른지 한 번 더 확인해주세요." 18 | } 19 | 20 | enum Favorite { 21 | static let titleLabelText = "찜한 상품이 없습니다." 22 | static let subTitleLableText = "마음에 드는 상품의 하트를 눌러 저장해 보세요." 23 | } 24 | } 25 | 26 | // MARK: - Private Property 27 | private let viewHolder: ViewHolder = .init() 28 | 29 | // MARK: - Initializer 30 | override init(frame: CGRect) { 31 | super.init(frame: .zero) 32 | 33 | configureView() 34 | configureConstraint() 35 | } 36 | 37 | required init?(coder: NSCoder) { 38 | fatalError("init(coder:) has not been implemented") 39 | } 40 | 41 | func setText(titleText: String, subTitleText: String) { 42 | viewHolder.titleLabel.text = titleText 43 | viewHolder.subtitleLabel.text = subTitleText 44 | } 45 | 46 | // MARK: - Private Method 47 | private func configureView() { 48 | viewHolder.place(in: contentView) 49 | } 50 | 51 | private func configureConstraint() { 52 | viewHolder.configureConstraints(for: contentView) 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/ProductFilter/ProductFilterBuilder.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ProductFilterBuilder.swift 3 | // Pyonsnal-Color 4 | // 5 | // Created by 김인호 on 2023/07/25. 6 | // 7 | 8 | import ModernRIBs 9 | 10 | protocol ProductFilterDependency: Dependency { 11 | // TODO: Declare the set of dependencies required by this RIB, but cannot be 12 | // created by this RIB. 13 | } 14 | 15 | final class ProductFilterComponent: Component { 16 | 17 | // TODO: Declare 'fileprivate' dependencies that are only used by this RIB. 18 | } 19 | 20 | // MARK: - Builder 21 | 22 | protocol ProductFilterBuildable: Buildable { 23 | func build( 24 | withListener listener: ProductFilterListener, 25 | filterEntity: FilterEntity 26 | ) -> ProductFilterRouting 27 | } 28 | 29 | final class ProductFilterBuilder: Builder, ProductFilterBuildable { 30 | 31 | override init(dependency: ProductFilterDependency) { 32 | super.init(dependency: dependency) 33 | } 34 | 35 | func build( 36 | withListener listener: ProductFilterListener, 37 | filterEntity: FilterEntity 38 | ) -> ProductFilterRouting { 39 | let component = ProductFilterComponent(dependency: dependency) 40 | let viewController = ProductFilterViewController(filterEntity: filterEntity) 41 | let interactor = ProductFilterInteractor(presenter: viewController) 42 | interactor.listener = listener 43 | return ProductFilterRouter(interactor: interactor, viewController: viewController) 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/ProfileEdit/ProfileEditBuilder.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ProfileEditBuilder.swift 3 | // Pyonsnal-Color 4 | // 5 | // Created by 조소정 on 11/16/23. 6 | // 7 | 8 | import ModernRIBs 9 | 10 | protocol ProfileEditDependency: Dependency { 11 | var memberAPIService: MemberAPIService { get } 12 | } 13 | 14 | final class ProfileEditComponent: Component { 15 | var memberInfo: MemberInfoEntity 16 | init(dependency: ProfileEditDependency, memberInfo: MemberInfoEntity) { 17 | self.memberInfo = memberInfo 18 | super.init(dependency: dependency) 19 | } 20 | } 21 | 22 | // MARK: - Builder 23 | 24 | protocol ProfileEditBuildable: Buildable { 25 | func build(withListener listener: ProfileEditListener, memberInfo: MemberInfoEntity) -> ProfileEditRouting 26 | } 27 | 28 | final class ProfileEditBuilder: Builder, ProfileEditBuildable { 29 | 30 | override init(dependency: ProfileEditDependency) { 31 | super.init(dependency: dependency) 32 | } 33 | 34 | func build( 35 | withListener listener: ProfileEditListener, 36 | memberInfo: MemberInfoEntity 37 | ) -> ProfileEditRouting { 38 | let component = ProfileEditComponent(dependency: dependency, memberInfo: memberInfo) 39 | let viewController = ProfileEditViewController() 40 | let interactor = ProfileEditInteractor(presenter: viewController, component: component) 41 | interactor.listener = listener 42 | return ProfileEditRouter(interactor: interactor, viewController: viewController) 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Resource/Assets.xcassets/Review/icon_review.imageset/icon_review.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Common/BottomSheet/BottomSheetDismissAnimator.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BottomSheetDismissAnimator.swift 3 | // Pyonsnal-Color 4 | // 5 | // Created by 김진우 on 2023/07/30. 6 | // 7 | 8 | import UIKit 9 | 10 | class BottomSheetDismissAnimator: NSObject { 11 | let animateDuration: CGFloat 12 | 13 | required init(with duration: CGFloat = 0.2) { 14 | self.animateDuration = duration 15 | } 16 | } 17 | 18 | extension BottomSheetDismissAnimator: UIViewControllerAnimatedTransitioning { 19 | func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval { 20 | animateDuration 21 | } 22 | 23 | func animateTransition(using transitionContext: UIViewControllerContextTransitioning) { 24 | // fromViewController is presented view controller 25 | guard let fromViewController = transitionContext.viewController(forKey: .from) else { return } 26 | 27 | let containerView = transitionContext.containerView 28 | let estimatedHeight = transitionContext.finalFrame(for: fromViewController).height 29 | 30 | UIView.animate( 31 | withDuration: animateDuration, 32 | delay: 0.0, 33 | options: .curveEaseIn, 34 | animations: { 35 | containerView.alpha = 0.0 36 | fromViewController.view.transform = CGAffineTransform(translationX: 0.0, y: estimatedHeight / 2.0) 37 | }, 38 | completion: { _ in 39 | transitionContext.completeTransition(!transitionContext.transitionWasCancelled) 40 | }) 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/ProductFilter/ProductFilterInteractor.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ProductFilterInteractor.swift 3 | // Pyonsnal-Color 4 | // 5 | // Created by 김인호 on 2023/07/25. 6 | // 7 | 8 | import ModernRIBs 9 | 10 | protocol ProductFilterRouting: ViewableRouting { 11 | } 12 | 13 | protocol ProductFilterPresentable: Presentable { 14 | var listener: ProductFilterPresentableListener? { get set } 15 | } 16 | 17 | protocol ProductFilterListener: AnyObject { 18 | func applyFilterItems(_ items: [FilterItemEntity], type: FilterType) 19 | func applySortFilter(item: FilterItemEntity) 20 | func productFilterDidTapCloseButton() 21 | } 22 | 23 | final class ProductFilterInteractor: PresentableInteractor, ProductFilterInteractable, ProductFilterPresentableListener { 24 | 25 | weak var router: ProductFilterRouting? 26 | weak var listener: ProductFilterListener? 27 | 28 | override init(presenter: ProductFilterPresentable) { 29 | super.init(presenter: presenter) 30 | presenter.listener = self 31 | } 32 | 33 | override func didBecomeActive() { 34 | super.didBecomeActive() 35 | } 36 | 37 | override func willResignActive() { 38 | super.willResignActive() 39 | } 40 | 41 | func didTapApplyButton(with selectedItems: [FilterItemEntity], type: FilterType) { 42 | listener?.applyFilterItems(selectedItems, type: type) 43 | } 44 | 45 | func didSelectSortFilter(item: FilterItemEntity) { 46 | listener?.applySortFilter(item: item) 47 | } 48 | 49 | func didTapCloseButton() { 50 | listener?.productFilterDidTapCloseButton() 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Common/ReusableView/ItemHeaderTitleView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ItemHeaderTitleView.swift 3 | // Pyonsnal-Color 4 | // 5 | // Created by 조소정 on 2023/06/10. 6 | // 7 | 8 | import UIKit 9 | import SnapKit 10 | 11 | final class ItemHeaderTitleView: UICollectionReusableView { 12 | 13 | enum Size { 14 | static let labelGreaterThanEqualLeading: CGFloat = 100 15 | } 16 | 17 | // MARK: - Private property 18 | private let viewHolder: ViewHolder = .init() 19 | 20 | // MARK: - Initializer 21 | override init(frame: CGRect) { 22 | super.init(frame: .zero) 23 | viewHolder.place(in: self) 24 | viewHolder.configureConstraints(for: self) 25 | } 26 | 27 | required init?(coder: NSCoder) { 28 | fatalError("init(coder:) has not been implemented") 29 | } 30 | 31 | func update(title: String) { 32 | viewHolder.titleLabel.text = title 33 | } 34 | } 35 | 36 | extension ItemHeaderTitleView { 37 | class ViewHolder: ViewHolderable { 38 | 39 | let titleLabel: UILabel = { 40 | let label = UILabel() 41 | label.font = .body2m 42 | return label 43 | }() 44 | 45 | func place(in view: UIView) { 46 | view.addSubview(titleLabel) 47 | } 48 | 49 | func configureConstraints(for view: UIView) { 50 | titleLabel.snp.makeConstraints { 51 | $0.leading.equalToSuperview().offset(.spacing16) 52 | $0.top.equalToSuperview().offset(.spacing24) 53 | $0.bottom.equalToSuperview() 54 | } 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Entity/Review/ReviewEvaluationKind.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ReviewEvaluationKind.swift 3 | // Pyonsnal-Color 4 | // 5 | // Created by 김진우 on 2023/09/29. 6 | // 7 | 8 | enum ReviewEvaluationKind { 9 | case taste 10 | case quality 11 | case valueForMoney 12 | 13 | var name: String { 14 | switch self { 15 | case .taste: 16 | return "맛" 17 | case .quality: 18 | return "퀄리티" 19 | case .valueForMoney: 20 | return "가격" 21 | } 22 | } 23 | 24 | var detailReviewTitle: String { 25 | switch self { 26 | case .taste: 27 | return "상품의 맛은 어떤가요?" 28 | case .quality: 29 | return "상품의 퀄리티는 어떤가요?" 30 | case .valueForMoney: 31 | return "상품의 가격은 어떤가요?" 32 | } 33 | } 34 | 35 | var goodStateText: String { 36 | switch self { 37 | case .taste: 38 | return "맛있어요" 39 | case .quality: 40 | return "좋아요" 41 | case .valueForMoney: 42 | return "합리적이에요" 43 | } 44 | } 45 | 46 | var normalStateText: String { 47 | switch self { 48 | case .taste: 49 | return "보통이에요" 50 | case .quality: 51 | return "보통이에요" 52 | case .valueForMoney: 53 | return "적당해요" 54 | } 55 | } 56 | 57 | var badStateText: String { 58 | switch self { 59 | case .taste: 60 | return "별로에요" 61 | case .quality: 62 | return "별로에요" 63 | case .valueForMoney: 64 | return "비싸요" 65 | } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Extension/String+.swift: -------------------------------------------------------------------------------- 1 | // 2 | // String+.swift 3 | // Pyonsnal-Color 4 | // 5 | // Created by 조소정 on 2023/06/14. 6 | // 7 | 8 | import UIKit 9 | 10 | extension String { 11 | /// 취소선을 그어줍니다. 12 | /// - Parameter color: 취소선 색 13 | func strikeThrough(with color: UIColor) -> NSMutableAttributedString { 14 | let attributedString = NSMutableAttributedString(string: self) 15 | let range = NSMakeRange(0, attributedString.length) 16 | 17 | attributedString.addAttribute( 18 | NSAttributedString.Key.strikethroughStyle, 19 | value: NSUnderlineStyle.single.rawValue, 20 | range: range 21 | ) 22 | attributedString.addAttribute( 23 | NSAttributedString.Key.strikethroughColor, 24 | value: color, 25 | range: range 26 | ) 27 | return attributedString 28 | } 29 | 30 | /// + "원" 31 | func addWon() -> String { 32 | return self + "원" 33 | } 34 | 35 | var encodedURLString: String? { 36 | return self.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) 37 | } 38 | 39 | var date: Date? { 40 | let dateFormatter = DateFormatter() 41 | dateFormatter.dateFormat = "yyyy-MM-dd" 42 | dateFormatter.timeZone = .current 43 | return dateFormatter.date(from: self) 44 | } 45 | 46 | func isBackSpace() -> Bool { 47 | if let char = self.cString(using: String.Encoding.utf8) { 48 | let isBackSpace = strcmp(char, "\\b") == -92 49 | if isBackSpace { return true } 50 | } 51 | return false 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/ProductDetail/Subview/ImageCell/ProductDetailImageCell+ViewHolder.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ProductDetailImageCell+ViewHolder.swift 3 | // Pyonsnal-Color 4 | // 5 | // Created by 김진우 on 2023/09/10. 6 | // 7 | 8 | import UIKit 9 | 10 | import SnapKit 11 | 12 | extension ProductDetailImageCell { 13 | final class ViewHolder: ViewHolderable { 14 | // MARK: - Declaration 15 | enum Size { 16 | static let imageViewSize: CGFloat = 200 17 | static let imageViewTopBottomInset: CGFloat = 60 18 | } 19 | 20 | // MARK: - UI Component 21 | let contentView: UIView = { 22 | let view = UIView(frame: .zero) 23 | return view 24 | }() 25 | 26 | let imageView: UIImageView = { 27 | let imageView = UIImageView(frame: .zero) 28 | imageView.contentMode = .scaleAspectFit 29 | return imageView 30 | }() 31 | 32 | // MARK: - Interface 33 | func place(in view: UIView) { 34 | view.addSubview(contentView) 35 | 36 | contentView.addSubview(imageView) 37 | } 38 | 39 | func configureConstraints(for view: UIView) { 40 | contentView.snp.makeConstraints { make in 41 | make.edges.equalToSuperview() 42 | } 43 | 44 | imageView.snp.makeConstraints { make in 45 | make.size.equalTo(Size.imageViewSize) 46 | make.centerX.equalToSuperview() 47 | make.top.bottom.equalToSuperview().inset(Size.imageViewTopBottomInset) 48 | } 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /Pyonsnal-Color/Pyonsnal-Color/Networking/NetworkRequestBuilder.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NetworkRequestBuilder.swift 3 | // Pyonsnal-Color 4 | // 5 | // Created by 조소정 on 2023/07/03. 6 | // 7 | 8 | import Foundation 9 | import Alamofire 10 | 11 | protocol NetworkRequestBuilder: URLRequestConvertible { 12 | var urlString: String { get } 13 | var path: String { get } 14 | var queryItems: [URLQueryItem]? { get } 15 | var method: HTTPMethod { get } 16 | var headers: [HTTPHeader]? { get } 17 | var body: [String: Any]? { get } 18 | } 19 | 20 | extension NetworkRequestBuilder { 21 | 22 | func asURLRequest() throws -> URLRequest { 23 | let baseURL = try urlString.asURL().appendingPathComponent(path, isDirectory: false) 24 | var component = URLComponents(url: baseURL, resolvingAgainstBaseURL: false) 25 | 26 | // set queryItem 27 | let urlQueryItems = queryItems?.compactMap { $0 } 28 | component?.queryItems = urlQueryItems 29 | 30 | let requestURL = component?.url ?? baseURL 31 | var urlRequest = URLRequest(url: requestURL) 32 | 33 | // set method 34 | urlRequest.httpMethod = method.rawValue 35 | 36 | // set body 37 | if let body { 38 | urlRequest.httpBody = try JSONSerialization.data(withJSONObject: body, 39 | options: []) 40 | } 41 | 42 | // set header 43 | if let headers { 44 | headers.forEach { header in 45 | urlRequest.addValue(header.value, forHTTPHeaderField: header.name) 46 | } 47 | } 48 | return urlRequest 49 | } 50 | } 51 | --------------------------------------------------------------------------------