├── .github
├── ISSUE_TEMPLATE
│ ├── bug-issue.md
│ └── feature-issue.md
└── pull_request_template.md
├── .gitignore
├── Projects
├── App
│ ├── App.xcodeproj
│ │ ├── project.pbxproj
│ │ ├── project.xcworkspace
│ │ │ └── contents.xcworkspacedata
│ │ ├── xcshareddata
│ │ │ └── xcschemes
│ │ │ │ └── App.xcscheme
│ │ └── xcuserdata
│ │ │ └── yeseul.xcuserdatad
│ │ │ └── xcschemes
│ │ │ └── xcschememanagement.plist
│ ├── InfoPlist.xcstrings
│ ├── Localizable.xcstrings
│ ├── Project.swift
│ ├── Resources
│ │ ├── Assets.xcassets
│ │ │ ├── AccentColor.colorset
│ │ │ │ └── Contents.json
│ │ │ ├── AppIcon.appiconset
│ │ │ │ ├── Contents.json
│ │ │ │ └── Instagram post - 9.png
│ │ │ ├── Contents.json
│ │ │ ├── logo.imageset
│ │ │ │ ├── Contents.json
│ │ │ │ ├── logo 1.svg
│ │ │ │ ├── logo 2.svg
│ │ │ │ ├── logo 3.svg
│ │ │ │ ├── logo 4.svg
│ │ │ │ ├── logo.png
│ │ │ │ ├── logo.svg
│ │ │ │ ├── logoDark 1.svg
│ │ │ │ ├── logoDark 2.svg
│ │ │ │ └── logoDark.svg
│ │ │ └── logo.png
│ │ ├── GoogleService-Info.plist
│ │ └── LaunchScreen.storyboard
│ ├── Seta.entitlements
│ ├── Setlist.xcodeproj
│ │ ├── project.pbxproj
│ │ └── xcuserdata
│ │ │ ├── a_mcflurry.xcuserdatad
│ │ │ └── xcschemes
│ │ │ │ └── xcschememanagement.plist
│ │ │ ├── kohyeji.xcuserdatad
│ │ │ └── xcschemes
│ │ │ │ └── xcschememanagement.plist
│ │ │ └── yeseul.xcuserdatad
│ │ │ └── xcschemes
│ │ │ └── xcschememanagement.plist
│ ├── Sources
│ │ └── App.swift
│ └── Support
│ │ └── Info.plist
├── Core
│ ├── Core.xcodeproj
│ │ ├── project.pbxproj
│ │ └── xcuserdata
│ │ │ ├── a_mcflurry.xcuserdatad
│ │ │ └── xcschemes
│ │ │ │ └── xcschememanagement.plist
│ │ │ ├── kohyeji.xcuserdatad
│ │ │ └── xcschemes
│ │ │ │ └── xcschememanagement.plist
│ │ │ └── yeseul.xcuserdatad
│ │ │ └── xcschemes
│ │ │ └── xcschememanagement.plist
│ ├── Project.swift
│ └── Sources
│ │ ├── Model
│ │ ├── AccessTokenModel.swift
│ │ ├── AccessTokenRequestModel.swift
│ │ ├── ArchivedConcertInfo
│ │ │ ├── ArchivedConcertInfo.swift
│ │ │ └── SaveSetlist.swift
│ │ ├── ArtistInfoModel.swift
│ │ ├── ArtistInfoModel
│ │ │ └── SaveArtistInfo.swift
│ │ ├── ArtistListModel.swift
│ │ ├── GeniusArtistsModel.swift
│ │ ├── GeniusSongsModel.swift
│ │ ├── LikeArtist
│ │ │ └── LikeArtistModel.swift
│ │ ├── OnboardingModel.swift
│ │ ├── SearchHistory
│ │ │ └── SearchHistoryModel.swift
│ │ ├── SearchModel.swift
│ │ └── SetlistListModel.swift
│ │ └── Service
│ │ ├── APIKeys.swift
│ │ ├── AnalyticsEvent.swift
│ │ ├── AppState.swift
│ │ ├── AppleMusicService.swift
│ │ ├── ArtistFetchService.swift
│ │ ├── NetworkManager.swift
│ │ ├── SearchHistoryDataManager
│ │ ├── ArtistDataManager.swift
│ │ └── KoreanConverter.swift
│ │ ├── SetlistDataService.swift
│ │ ├── SpotifyManager
│ │ └── SpotifyManager.swift
│ │ ├── SwiftDataManager
│ │ └── SwiftDataManager.swift
│ │ └── UserDefaultsManger.swift
├── Feature
│ ├── Feature.xcodeproj
│ │ ├── project.pbxproj
│ │ └── xcuserdata
│ │ │ ├── a_mcflurry.xcuserdatad
│ │ │ └── xcschemes
│ │ │ │ └── xcschememanagement.plist
│ │ │ ├── kohyeji.xcuserdatad
│ │ │ └── xcschemes
│ │ │ │ └── xcschememanagement.plist
│ │ │ └── yeseul.xcuserdatad
│ │ │ └── xcschemes
│ │ │ └── xcschememanagement.plist
│ ├── Localizable.xcstrings
│ ├── Project.swift
│ └── Scenes
│ │ ├── ArchiveScene
│ │ ├── Component
│ │ │ ├── ArchiveArtistCell.swift
│ │ │ ├── ArchiveConcertInfoCell.swift
│ │ │ ├── ArtistSetCell.swift
│ │ │ ├── EmptyArtistImage.swift
│ │ │ ├── IsEmptyCell.swift
│ │ │ └── MenuButton.swift
│ │ ├── View
│ │ │ ├── ArchivingArtistView.swift
│ │ │ └── ArchivingView.swift
│ │ └── ViewModel
│ │ │ └── ArchivingViewModel.swift
│ │ ├── ArtistScene
│ │ ├── View
│ │ │ ├── AllSetlistsView.swift
│ │ │ ├── ArtistInfoView.swift
│ │ │ ├── ArtistView.swift
│ │ │ └── BookmarkedSetlistsView.swift
│ │ └── ViewModel
│ │ │ └── ArtistViewModel.swift
│ │ ├── MainScene
│ │ ├── Component
│ │ │ ├── ArtistImage.swift
│ │ │ ├── ArtistMainSetlistView.swift
│ │ │ ├── ArtistNameView.swift
│ │ │ ├── ArtistsContentView.swift
│ │ │ ├── EmptyMainView.swift
│ │ │ └── MainToolTipView.swift
│ │ ├── View
│ │ │ ├── MainView.swift
│ │ │ └── TabBarView.swift
│ │ └── ViewModel
│ │ │ └── MainViewModel.swift
│ │ ├── NetworkUnavailableView.swift
│ │ ├── OnboardingScene
│ │ ├── View
│ │ │ └── OnboardingView.swift
│ │ └── ViewModel
│ │ │ └── OnboardingViewModel.swift
│ │ ├── SearchScene
│ │ ├── Component
│ │ │ ├── SearchArtistCell.swift
│ │ │ ├── SearchArtistList.swift
│ │ │ ├── SearchHistoryCell.swift
│ │ │ └── SearchbarCustom.swift
│ │ ├── View
│ │ │ └── SearchView.swift
│ │ └── ViewModel
│ │ │ └── SearchViewModel.swift
│ │ ├── SetlistScene
│ │ ├── Component
│ │ │ ├── CaptureSetlist
│ │ │ │ └── CaptureSetlistView.swift
│ │ │ ├── CustomTitleAlert.swift
│ │ │ ├── ExportPlaylistButtonView.swift
│ │ │ ├── MusicButtonView.swift
│ │ │ ├── SetlistFMLinkButtonView.swift
│ │ │ ├── ShareOptionButtonView.swift
│ │ │ ├── ShareSetlistView.swift
│ │ │ └── ToastMessageView.swift
│ │ ├── View
│ │ │ ├── BottomModalView.swift
│ │ │ ├── EmptySetlistView.swift
│ │ │ ├── ListView.swift
│ │ │ ├── SetlistImageShareView.swift
│ │ │ ├── SetlistInfoView.swift
│ │ │ └── SetlistView.swift
│ │ └── ViewModel
│ │ │ ├── ExportPlaylistViewModel.swift
│ │ │ └── SetlistViewModel.swift
│ │ ├── SettingScene
│ │ └── View
│ │ │ ├── AskView.swift
│ │ │ └── SettingView.swift
│ │ └── SummarizedSetlistInfoView.swift
└── UI
│ ├── .DS_Store
│ ├── Project.swift
│ ├── Resources
│ ├── Colors.xcassets
│ │ ├── Contents.json
│ │ ├── ellipsis.colorset
│ │ │ └── Contents.json
│ │ ├── gray6.colorset
│ │ │ └── Contents.json
│ │ ├── gray600.colorset
│ │ │ └── Contents.json
│ │ ├── mainBlack.colorset
│ │ │ └── Contents.json
│ │ ├── mainOrange.colorset
│ │ │ └── Contents.json
│ │ ├── mainWhite.colorset
│ │ │ └── Contents.json
│ │ ├── orange100.colorset
│ │ │ └── Contents.json
│ │ ├── shareBG.colorset
│ │ │ └── Contents.json
│ │ ├── toast1.colorset
│ │ │ └── Contents.json
│ │ ├── toast2.colorset
│ │ │ └── Contents.json
│ │ └── toastBG.colorset
│ │ │ └── Contents.json
│ └── Images.xcassets
│ │ ├── AppImages
│ │ ├── Contents.json
│ │ ├── appleMusic.imageset
│ │ │ ├── Contents.json
│ │ │ └── appleMusic.png
│ │ ├── spotify.imageset
│ │ │ ├── Contents.json
│ │ │ └── spotify.png
│ │ └── youtubeMusic.imageset
│ │ │ ├── Contents.json
│ │ │ └── youtubeMusic.png
│ │ ├── Contents.json
│ │ ├── ForeignArtist
│ │ ├── AJR.imageset
│ │ │ ├── AJR.jpg
│ │ │ └── Contents.json
│ │ ├── BrunoMars.imageset
│ │ │ ├── BrunoMars.jpg
│ │ │ └── Contents.json
│ │ ├── Contents.json
│ │ ├── DojaCat.imageset
│ │ │ ├── Contents.json
│ │ │ └── DojaCat.jpg
│ │ ├── Drake.imageset
│ │ │ ├── Contents.json
│ │ │ └── Drake.png
│ │ ├── DuaLipa.imageset
│ │ │ ├── Contents.json
│ │ │ └── DuaLipa.jpg
│ │ ├── Eminem.imageset
│ │ │ ├── Contents.json
│ │ │ └── Eminem.jpg
│ │ ├── JohnK.imageset
│ │ │ ├── Contents.json
│ │ │ └── JohnK.jpg
│ │ ├── Maroon5.imageset
│ │ │ ├── Contents.json
│ │ │ └── Maroon5.jpg
│ │ ├── PostMalone.imageset
│ │ │ ├── Contents.json
│ │ │ └── PostMalone.jpg
│ │ ├── SamSmith.imageset
│ │ │ ├── Contents.json
│ │ │ └── SamSmith.jpg
│ │ ├── TheWeekend.imageset
│ │ │ ├── Contents.json
│ │ │ └── TheWeekend.jpg
│ │ └── TroyeSivan.imageset
│ │ │ ├── Contents.json
│ │ │ └── TroyeSivan.jpg
│ │ ├── KoreanArtist
│ │ ├── BTS.imageset
│ │ │ ├── BTS.jpg
│ │ │ └── Contents.json
│ │ ├── CarTheGarden.imageset
│ │ │ ├── CarTheGarden.jpg
│ │ │ └── Contents.json
│ │ ├── Contents.json
│ │ ├── DAY6.imageset
│ │ │ ├── Contents.json
│ │ │ └── DAY6.jpg
│ │ ├── JayPark.imageset
│ │ │ ├── Contents.json
│ │ │ └── JayPark.jpg
│ │ ├── LeeYoungJi.imageset
│ │ │ ├── Contents.json
│ │ │ └── LeeYoungJi.jpg
│ │ ├── NCTDream.imageset
│ │ │ ├── Contents.json
│ │ │ └── NCTDream.jpg
│ │ ├── NewJeans.imageset
│ │ │ ├── Contents.json
│ │ │ └── NewJeans.jpg
│ │ ├── PaulKim.imageset
│ │ │ ├── Contents.json
│ │ │ └── PaulKim.jpg
│ │ ├── StrayKids.imageset
│ │ │ ├── Contents.json
│ │ │ └── StrayKids.jpg
│ │ ├── Woodz.imageset
│ │ │ ├── Contents.json
│ │ │ └── Woodz.jpg
│ │ └── Yoonha.imageset
│ │ │ ├── Contents.json
│ │ │ └── Yoonha.jpg
│ │ ├── archiveIcon.imageset
│ │ ├── Contents.json
│ │ └── archiveIcon.svg
│ │ ├── artistViewTicket.imageset
│ │ ├── Contents.json
│ │ ├── Group 1000002051.png
│ │ └── darkArtistViewTicket.png
│ │ ├── darkArtistViewTicket.imageset
│ │ ├── Contents.json
│ │ └── darkArtistViewTicket.png
│ │ ├── darkTicket.imageset
│ │ ├── Contents.json
│ │ └── darkTicket.png
│ │ ├── homeIcon.imageset
│ │ ├── Contents.json
│ │ └── homeIcon.svg
│ │ ├── mainViewTicket.imageset
│ │ ├── Contents.json
│ │ └── Ticket.svg
│ │ ├── modalCloseButton.imageset
│ │ ├── Contents.json
│ │ └── modal_close_button.png
│ │ ├── screenshotforOCR.imageset
│ │ ├── Contents.json
│ │ └── screenshotforOCR.png
│ │ ├── searchIcon.imageset
│ │ ├── Contents.json
│ │ └── searchIcon.svg
│ │ ├── serviceForConcertgoers.imageset
│ │ ├── Contents.json
│ │ └── serviceForConcertgoers.png
│ │ ├── seta.imageset
│ │ ├── Contents.json
│ │ └── Seta.svg
│ │ ├── ticket.imageset
│ │ ├── Contents.json
│ │ └── ticket.png
│ │ └── whiteTicket.imageset
│ │ ├── Contents.json
│ │ └── whiteTicket.png
│ ├── Sources
│ └── Extensions
│ │ ├── ArchiveExtension.swift
│ │ ├── CellFrameExtension.swift
│ │ ├── ColorExtension.swift
│ │ ├── DateFormatterExtension.swift
│ │ ├── ImageCropExtension.swift
│ │ └── UIScreenExtension.swift
│ └── UI.xcodeproj
│ ├── project.pbxproj
│ └── xcuserdata
│ ├── a_mcflurry.xcuserdatad
│ └── xcschemes
│ │ └── xcschememanagement.plist
│ ├── choihyowon.xcuserdatad
│ └── xcschemes
│ │ └── xcschememanagement.plist
│ ├── kohyeji.xcuserdatad
│ └── xcschemes
│ │ └── xcschememanagement.plist
│ └── yeseul.xcuserdatad
│ └── xcschemes
│ └── xcschememanagement.plist
├── README.md
├── Setlist.xcworkspace
├── contents.xcworkspacedata
├── xcshareddata
│ └── xcschemes
│ │ └── Setlist-Workspace.xcscheme
└── xcuserdata
│ ├── a_mcflurry.xcuserdatad
│ ├── UserInterfaceState.xcuserstate
│ ├── xcdebugger
│ │ └── Breakpoints_v2.xcbkptlist
│ └── xcschemes
│ │ └── xcschememanagement.plist
│ ├── choihyowon.xcuserdatad
│ └── UserInterfaceState.xcuserstate
│ ├── kohyeji.xcuserdatad
│ ├── UserInterfaceState.xcuserstate
│ ├── xcdebugger
│ │ └── Breakpoints_v2.xcbkptlist
│ └── xcschemes
│ │ └── xcschememanagement.plist
│ └── yeseul.xcuserdatad
│ ├── UserInterfaceState.xcuserstate
│ ├── xcdebugger
│ └── Breakpoints_v2.xcbkptlist
│ └── xcschemes
│ └── xcschememanagement.plist
├── Tuist
├── Config.swift
└── ProjectDescriptionHelpers
│ ├── Dependency+Project.swift
│ ├── Dependency+Spm.swift
│ └── Project+Templates.swift
└── Workspace.swift
/.github/ISSUE_TEMPLATE/bug-issue.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: bug issue
3 | about: IssueTemplate
4 | title: ''
5 | labels: ''
6 | assignees: ''
7 |
8 | ---
9 |
10 | ## 🐛 Description
11 | [이런 상황]에서 [이런 버그]가 발생합니다.
12 |
13 | ## 📝 TODO
14 | - [ ]
15 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature-issue.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: feature issue
3 | about: IssueTemplate
4 | title: ''
5 | labels: ''
6 | assignees: ''
7 |
8 | ---
9 |
10 | ## 🛠 Issue
11 | -
12 |
13 | ## 📝 To-do
14 | - [ ]
15 |
--------------------------------------------------------------------------------
/.github/pull_request_template.md:
--------------------------------------------------------------------------------
1 | ## 🔥 *Pull requests*
2 |
3 | ⛳️ **작업한 브랜치**
4 | - #
5 |
6 | 👷 **작업한 내용**
7 | -
8 |
9 | ## 🚨 참고 사항
10 | -
11 |
12 | ## 📸 스크린샷
13 | |기능|스크린샷|
14 | |:--:|:--:|
15 | |||
16 |
17 | ## 📟 관련 이슈
18 | - Resolved: #
19 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | ### macOS ###
2 | # General
3 | .DS_Store
4 | .AppleDouble
5 | .LSOverride
6 |
7 | # Icon must end with two
8 | Icon
9 |
10 | # Thumbnails
11 | ._*
12 |
13 | # Files that might appear in the root of a volume
14 | .DocumentRevisions-V100
15 | .fseventsd
16 | .Spotlight-V100
17 | .TemporaryItems
18 | .Trashes
19 | .VolumeIcon.icns
20 | .com.apple.timemachine.donotpresent
21 |
22 | # Directories potentially created on remote AFP share
23 | .AppleDB
24 | .AppleDesktop
25 | Network Trash Folder
26 | Temporary Items
27 | .apdisk
28 |
29 | ### Xcode ###
30 | # Xcode
31 | #
32 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore
33 |
34 | ## User settings
35 | xcuserdata/
36 |
37 | ## compatibility with Xcode 8 and earlier (ignoring not required starting Xcode 9)
38 | *.xcscmblueprint
39 | *.xccheckout
40 |
41 | ## compatibility with Xcode 3 and earlier (ignoring not required starting Xcode 4)
42 | build/
43 | DerivedData/
44 | *.moved-aside
45 | *.pbxuser
46 | !default.pbxuser
47 | *.mode1v3
48 | !default.mode1v3
49 | *.mode2v3
50 | !default.mode2v3
51 | *.perspectivev3
52 | !default.perspectivev3
53 |
54 | ### Xcode Patch ###
55 | *.xcodeproj/*
56 | !*.xcodeproj/project.pbxproj
57 | !*.xcodeproj/xcshareddata/
58 | !*.xcworkspace/contents.xcworkspacedata
59 | /*.gcno
60 |
61 | ### Projects ###
62 | *.xcodeproj
63 | *.xcworkspace
64 |
65 | ### Tuist derived files ###
66 | graph.dot
67 | Derived/
68 |
69 | ### Tuist managed dependencies ###
70 | Tuist/Dependencies
71 |
72 | APIKeys.swift
73 |
--------------------------------------------------------------------------------
/Projects/App/App.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/Projects/App/App.xcodeproj/xcshareddata/xcschemes/App.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
32 |
33 |
35 |
41 |
42 |
43 |
44 |
45 |
51 |
52 |
53 |
54 |
65 |
67 |
73 |
74 |
75 |
76 |
82 |
84 |
90 |
91 |
92 |
93 |
95 |
96 |
99 |
100 |
101 |
--------------------------------------------------------------------------------
/Projects/App/App.xcodeproj/xcuserdata/yeseul.xcuserdatad/xcschemes/xcschememanagement.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | SchemeUserState
6 |
7 | App.xcscheme_^#shared#^_
8 |
9 | isShown
10 |
11 | orderHint
12 | 0
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/Projects/App/InfoPlist.xcstrings:
--------------------------------------------------------------------------------
1 | {
2 | "sourceLanguage" : "en",
3 | "strings" : {
4 | "CFBundleName" : {
5 | "comment" : "Bundle name",
6 | "extractionState" : "extracted_with_value",
7 | "localizations" : {
8 | "en" : {
9 | "stringUnit" : {
10 | "state" : "new",
11 | "value" : "Seta"
12 | }
13 | }
14 | }
15 | },
16 | "NSAppleMusicUsageDescription" : {
17 | "extractionState" : "manual",
18 | "localizations" : {
19 | "en" : {
20 | "stringUnit" : {
21 | "state" : "translated",
22 | "value" : "To enable the Export Playlist feature, You must allow access to 'Apple Music'"
23 | }
24 | },
25 | "ko" : {
26 | "stringUnit" : {
27 | "state" : "needs_review",
28 | "value" : "공연의 세트리스트를 Apple Music 플레이리스트에 넣기 위해 접근을 허용해주세요"
29 | }
30 | }
31 | }
32 | },
33 | "NSPhotoLibraryUsageDescription" : {
34 | "extractionState" : "manual",
35 | "localizations" : {
36 | "en" : {
37 | "stringUnit" : {
38 | "state" : "translated",
39 | "value" : "To use the Photography feature, You must allow 'Photo/Video' access"
40 | }
41 | },
42 | "ko" : {
43 | "stringUnit" : {
44 | "state" : "needs_review",
45 | "value" : "세트리스트를 사진첩에 저장하려면 권한이 필요합니다."
46 | }
47 | }
48 | }
49 | }
50 | },
51 | "version" : "1.0"
52 | }
--------------------------------------------------------------------------------
/Projects/App/Project.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Project.swift
3 | // ProjectDescriptionHelpers
4 | //
5 | // Created by 최효원 on 2023/10/06.
6 | //
7 |
8 | import ProjectDescription
9 | import ProjectDescriptionHelpers
10 |
11 | let project = Project.makeModule(
12 | name: "Seta",
13 | platform: .iOS,
14 | product: .app,
15 | packages: [
16 | .Firebase,
17 | .SpotifyAPI,
18 | .KeychainAccess
19 | ],
20 | dependencies: [
21 | .Projcet.Feature,
22 | .SPM.Firebase,
23 | .SPM.SpotifyAPI,
24 | .SPM.KeychainAccess
25 | ],
26 | sources: ["Sources/**"],
27 | resources: ["Resources/**"],
28 | infoPlist: .file(path: "Support/Info.plist"))
29 |
--------------------------------------------------------------------------------
/Projects/App/Resources/Assets.xcassets/AccentColor.colorset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "colors" : [
3 | {
4 | "color" : {
5 | "color-space" : "srgb",
6 | "components" : {
7 | "alpha" : "1.000",
8 | "blue" : "0x00",
9 | "green" : "0x00",
10 | "red" : "0x00"
11 | }
12 | },
13 | "idiom" : "universal"
14 | },
15 | {
16 | "appearances" : [
17 | {
18 | "appearance" : "luminosity",
19 | "value" : "dark"
20 | }
21 | ],
22 | "color" : {
23 | "color-space" : "srgb",
24 | "components" : {
25 | "alpha" : "1.000",
26 | "blue" : "0xEE",
27 | "green" : "0xEA",
28 | "red" : "0xE9"
29 | }
30 | },
31 | "idiom" : "universal"
32 | }
33 | ],
34 | "info" : {
35 | "author" : "xcode",
36 | "version" : 1
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/Projects/App/Resources/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "Instagram post - 9.png",
5 | "idiom" : "universal",
6 | "platform" : "ios",
7 | "size" : "1024x1024"
8 | }
9 | ],
10 | "info" : {
11 | "author" : "xcode",
12 | "version" : 1
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/Projects/App/Resources/Assets.xcassets/AppIcon.appiconset/Instagram post - 9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DeveloperAcademy-POSTECH/2023-MacC-Team12-Creative8/4b68cf6d2cee5683693269560ffbd23162034ec0/Projects/App/Resources/Assets.xcassets/AppIcon.appiconset/Instagram post - 9.png
--------------------------------------------------------------------------------
/Projects/App/Resources/Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/Projects/App/Resources/Assets.xcassets/logo.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "logo.png",
5 | "idiom" : "universal",
6 | "scale" : "1x"
7 | },
8 | {
9 | "appearances" : [
10 | {
11 | "appearance" : "luminosity",
12 | "value" : "light"
13 | }
14 | ],
15 | "filename" : "logo 2.svg",
16 | "idiom" : "universal",
17 | "scale" : "1x"
18 | },
19 | {
20 | "appearances" : [
21 | {
22 | "appearance" : "luminosity",
23 | "value" : "dark"
24 | }
25 | ],
26 | "filename" : "logoDark.svg",
27 | "idiom" : "universal",
28 | "scale" : "1x"
29 | },
30 | {
31 | "filename" : "logo.svg",
32 | "idiom" : "universal",
33 | "scale" : "2x"
34 | },
35 | {
36 | "appearances" : [
37 | {
38 | "appearance" : "luminosity",
39 | "value" : "light"
40 | }
41 | ],
42 | "filename" : "logo 3.svg",
43 | "idiom" : "universal",
44 | "scale" : "2x"
45 | },
46 | {
47 | "appearances" : [
48 | {
49 | "appearance" : "luminosity",
50 | "value" : "dark"
51 | }
52 | ],
53 | "filename" : "logoDark 1.svg",
54 | "idiom" : "universal",
55 | "scale" : "2x"
56 | },
57 | {
58 | "filename" : "logo 1.svg",
59 | "idiom" : "universal",
60 | "scale" : "3x"
61 | },
62 | {
63 | "appearances" : [
64 | {
65 | "appearance" : "luminosity",
66 | "value" : "light"
67 | }
68 | ],
69 | "filename" : "logo 4.svg",
70 | "idiom" : "universal",
71 | "scale" : "3x"
72 | },
73 | {
74 | "appearances" : [
75 | {
76 | "appearance" : "luminosity",
77 | "value" : "dark"
78 | }
79 | ],
80 | "filename" : "logoDark 2.svg",
81 | "idiom" : "universal",
82 | "scale" : "3x"
83 | }
84 | ],
85 | "info" : {
86 | "author" : "xcode",
87 | "version" : 1
88 | }
89 | }
90 |
--------------------------------------------------------------------------------
/Projects/App/Resources/Assets.xcassets/logo.imageset/logo 1.svg:
--------------------------------------------------------------------------------
1 |
5 |
--------------------------------------------------------------------------------
/Projects/App/Resources/Assets.xcassets/logo.imageset/logo 2.svg:
--------------------------------------------------------------------------------
1 |
5 |
--------------------------------------------------------------------------------
/Projects/App/Resources/Assets.xcassets/logo.imageset/logo 3.svg:
--------------------------------------------------------------------------------
1 |
5 |
--------------------------------------------------------------------------------
/Projects/App/Resources/Assets.xcassets/logo.imageset/logo 4.svg:
--------------------------------------------------------------------------------
1 |
5 |
--------------------------------------------------------------------------------
/Projects/App/Resources/Assets.xcassets/logo.imageset/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DeveloperAcademy-POSTECH/2023-MacC-Team12-Creative8/4b68cf6d2cee5683693269560ffbd23162034ec0/Projects/App/Resources/Assets.xcassets/logo.imageset/logo.png
--------------------------------------------------------------------------------
/Projects/App/Resources/Assets.xcassets/logo.imageset/logo.svg:
--------------------------------------------------------------------------------
1 |
5 |
--------------------------------------------------------------------------------
/Projects/App/Resources/Assets.xcassets/logo.imageset/logoDark 1.svg:
--------------------------------------------------------------------------------
1 |
5 |
--------------------------------------------------------------------------------
/Projects/App/Resources/Assets.xcassets/logo.imageset/logoDark 2.svg:
--------------------------------------------------------------------------------
1 |
5 |
--------------------------------------------------------------------------------
/Projects/App/Resources/Assets.xcassets/logo.imageset/logoDark.svg:
--------------------------------------------------------------------------------
1 |
5 |
--------------------------------------------------------------------------------
/Projects/App/Resources/Assets.xcassets/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DeveloperAcademy-POSTECH/2023-MacC-Team12-Creative8/4b68cf6d2cee5683693269560ffbd23162034ec0/Projects/App/Resources/Assets.xcassets/logo.png
--------------------------------------------------------------------------------
/Projects/App/Resources/GoogleService-Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | API_KEY
6 | AIzaSyCCgP87-LaBtaVdwsmoAxwpTl_b8MLbiRY
7 | GCM_SENDER_ID
8 | 126154503966
9 | PLIST_VERSION
10 | 1
11 | BUNDLE_ID
12 | com.creative8.seta.Seta
13 | PROJECT_ID
14 | seta-e6522
15 | STORAGE_BUCKET
16 | seta-e6522.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:126154503966:ios:27c1f4b0d3093c2b45fb0f
29 |
30 |
--------------------------------------------------------------------------------
/Projects/App/Resources/LaunchScreen.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
--------------------------------------------------------------------------------
/Projects/App/Seta.entitlements:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | aps-environment
6 | development
7 | com.apple.developer.icloud-container-identifiers
8 |
9 | iCloud.creative8.Seta
10 |
11 | com.apple.developer.icloud-services
12 |
13 | CloudDocuments
14 | CloudKit
15 |
16 | com.apple.developer.ubiquity-container-identifiers
17 |
18 | iCloud.creative8.Seta
19 |
20 | com.apple.security.app-sandbox
21 |
22 | keychain-access-groups
23 |
24 | $(AppIdentifierPrefix)com.creative8.seta.Seta
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/Projects/App/Setlist.xcodeproj/xcuserdata/a_mcflurry.xcuserdatad/xcschemes/xcschememanagement.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | SchemeUserState
6 |
7 | Setlist.xcscheme_^#shared#^_
8 |
9 | isShown
10 |
11 | orderHint
12 | 2
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/Projects/App/Setlist.xcodeproj/xcuserdata/kohyeji.xcuserdatad/xcschemes/xcschememanagement.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | SchemeUserState
6 |
7 | Setlist.xcscheme_^#shared#^_
8 |
9 | isShown
10 |
11 | orderHint
12 | 2
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/Projects/App/Setlist.xcodeproj/xcuserdata/yeseul.xcuserdatad/xcschemes/xcschememanagement.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | SchemeUserState
6 |
7 | Setlist.xcscheme_^#shared#^_
8 |
9 | isShown
10 |
11 | orderHint
12 | 2
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/Projects/App/Sources/App.swift:
--------------------------------------------------------------------------------
1 | //
2 | // App.swift
3 | // ProjectDescriptionHelpers
4 | //
5 | // Created by 최효원 on 2023/10/06.
6 | //
7 |
8 | import SwiftUI
9 | import SwiftData
10 | import Feature
11 | import Core
12 | import Firebase
13 |
14 | @main
15 | struct SetlistApp: App {
16 | @State var networkMonitor = NetworkMonitor()
17 | @StateObject private var appState = AppState()
18 |
19 | var sharedModelContainer: ModelContainer = {
20 | let schema = Schema([
21 | ArchivedConcertInfo.self, LikeArtist.self, SearchHistory.self
22 | ])
23 | let modelConfiguration = ModelConfiguration(schema: schema, isStoredInMemoryOnly: false)
24 |
25 | do {
26 | return try ModelContainer(for: schema, configurations: [modelConfiguration])
27 | } catch {
28 | fatalError("Could not create ModelContainer: \(error)")
29 | }
30 | }()
31 |
32 | init() {
33 | FirebaseApp.configure()
34 | Thread.sleep(forTimeInterval: 2)
35 | }
36 |
37 | var body: some Scene {
38 | WindowGroup {
39 | if appState.isOnboarding {
40 | OnboardingView()
41 | .environmentObject(appState)
42 | .onAppear() {
43 | AnalyticsEvent.trackScreen(screenName: AnalyticsEvent.Screen.onboarding, screenClass: "OnboardingView")
44 | }
45 | } else {
46 | TabBarView()
47 | }
48 | }
49 | .modelContainer(sharedModelContainer)
50 | .environmentObject(networkMonitor)
51 |
52 | }
53 | }
54 |
55 | extension UINavigationController {
56 | open override func viewWillLayoutSubviews() {
57 | navigationBar.topItem?.backBarButtonItem = UIBarButtonItem(title: "", style: .plain, target: nil, action: nil)
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/Projects/App/Support/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleLocalizations
14 |
15 | ko
16 |
17 | CFBundleName
18 | $(PRODUCT_NAME)
19 | CFBundlePackageType
20 | $(PRODUCT_BUNDLE_PACKAGE_TYPE)
21 | CFBundleShortVersionString
22 | 2.0.0
23 | CFBundleURLTypes
24 |
25 |
26 | CFBundleURLSchemes
27 |
28 | fbAPP-ID
29 |
30 |
31 |
32 | CFBundleTypeRole
33 | Editor
34 | CFBundleURLName
35 | com.create8.seta.Seta
36 | CFBundleURLSchemes
37 |
38 | seta-login
39 |
40 |
41 |
42 | CFBundleVersion
43 | 2
44 | LSApplicationQueriesSchemes
45 |
46 | instagram-stories
47 |
48 | LSRequiresIPhoneOS
49 |
50 | NSAppleMusicUsageDescription
51 | NSAppleMusicUsageDescription
52 | NSPhotoLibraryUsageDescription
53 | NSPhotoLibraryUsageDescription
54 | UIApplicationSceneManifest
55 |
56 | UIApplicationSupportsMultipleScenes
57 |
58 | UISceneConfigurations
59 |
60 | UIWindowSceneSessionRoleApplication
61 |
62 |
63 | UISceneConfigurationName
64 | Default Configuration
65 | UISceneDelegateClassName
66 | $(PRODUCT_MODULE_NAME).SceneDelegate
67 |
68 |
69 |
70 |
71 | UIApplicationSupportsIndirectInputEvents
72 |
73 | UIBackgroundModes
74 |
75 | remote-notification
76 |
77 | UILaunchStoryboardName
78 | LaunchScreen
79 | UIRequiredDeviceCapabilities
80 |
81 | armv7
82 |
83 | UISupportedInterfaceOrientations
84 |
85 | UIInterfaceOrientationPortrait
86 |
87 | UISupportedInterfaceOrientations~ipad
88 |
89 | UIInterfaceOrientationLandscapeLeft
90 | UIInterfaceOrientationLandscapeRight
91 | UIInterfaceOrientationPortrait
92 | UIInterfaceOrientationPortraitUpsideDown
93 |
94 |
95 |
96 |
--------------------------------------------------------------------------------
/Projects/Core/Core.xcodeproj/xcuserdata/a_mcflurry.xcuserdatad/xcschemes/xcschememanagement.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | SchemeUserState
6 |
7 | Core.xcscheme_^#shared#^_
8 |
9 | isShown
10 |
11 | orderHint
12 | 0
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/Projects/Core/Core.xcodeproj/xcuserdata/kohyeji.xcuserdatad/xcschemes/xcschememanagement.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | SchemeUserState
6 |
7 | Core.xcscheme_^#shared#^_
8 |
9 | isShown
10 |
11 | orderHint
12 | 0
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/Projects/Core/Core.xcodeproj/xcuserdata/yeseul.xcuserdatad/xcschemes/xcschememanagement.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | SchemeUserState
6 |
7 | Core.xcscheme_^#shared#^_
8 |
9 | isShown
10 |
11 | orderHint
12 | 0
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/Projects/Core/Project.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Project.swift
3 | // ProjectDescriptionHelpers
4 | //
5 | // Created by 최효원 on 2023/10/06.
6 | //
7 |
8 | import ProjectDescription
9 | import ProjectDescriptionHelpers
10 |
11 | let project = Project.makeModule(
12 | name: "Core",
13 | product: .staticFramework,
14 | dependencies: [
15 | .SPM.KeychainAccess,
16 | .SPM.SpotifyAPI
17 | ],
18 | sources: ["Sources/**"]
19 | )
20 |
--------------------------------------------------------------------------------
/Projects/Core/Sources/Model/AccessTokenModel.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AccessTokenModel.swift
3 | // Core
4 | //
5 | // Created by A_Mcflurry on 6/30/24.
6 | // Copyright © 2024 com.creative8.seta. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | public struct AccessTokenModel: Decodable {
12 | private let accessToken: String
13 | private let tokenType: String
14 |
15 | public var token: String {
16 | return "\(tokenType) \(accessToken)"
17 | }
18 |
19 | enum CodingKeys: String, CodingKey {
20 | case accessToken = "access_token"
21 | case tokenType = "token_type"
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/Projects/Core/Sources/Model/AccessTokenRequestModel.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AccessTokenRequestModel.swift
3 | // Core
4 | //
5 | // Created by A_Mcflurry on 6/30/24.
6 | // Copyright © 2024 com.creative8.seta. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
--------------------------------------------------------------------------------
/Projects/Core/Sources/Model/ArchivedConcertInfo/ArchivedConcertInfo.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ArchivedConcertInfo.swift
3 | // Core
4 | //
5 | // Created by A_Mcflurry on 10/22/23.
6 | // Copyright © 2023 com.creative8.seta. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import SwiftData
11 |
12 | @Model
13 | public class ArchivedConcertInfo {
14 | public var id: UUID?
15 | public var artistInfo: SaveArtistInfo = SaveArtistInfo(name: "", country: "", alias: "", mbid: "", gid: 0, imageUrl: "", songList: [])
16 | public var setlist: SaveSetlist = SaveSetlist(setlistId: "", date: Date(), venue: "", title: "", city: "", country: "")
17 |
18 | public init(artistInfo: SaveArtistInfo, setlist: SaveSetlist) {
19 | self.id = UUID()
20 | self.artistInfo = artistInfo
21 | self.setlist = setlist
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/Projects/Core/Sources/Model/ArchivedConcertInfo/SaveSetlist.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SaveSetlist.swift
3 | // Core
4 | //
5 | // Created by A_Mcflurry on 10/22/23.
6 | // Copyright © 2023 com.creative8.seta. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | public struct SaveSetlist: Codable {
12 | public var setlistId: String
13 | public var date: Date
14 | public var venue: String
15 | public var title: String
16 | public var city: String
17 | public var country: String
18 |
19 | public init(setlistId: String, date: Date, venue: String, title: String, city: String, country: String) {
20 | self.setlistId = setlistId
21 | self.date = date
22 | self.venue = venue
23 | self.title = title
24 | self.city = city
25 | self.country = country
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/Projects/Core/Sources/Model/ArtistInfoModel.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ArtistInfoModel.swift
3 | // Core
4 | //
5 | // Created by 고혜지 on 10/18/23.
6 | // Copyright © 2023 com.creative8. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | public struct ArtistInfo {
12 | public var name: String
13 | public var alias: String?
14 | public var mbid: String
15 | public var gid: Int?
16 | public var imageUrl: String?
17 | public var songList: [Titles]?
18 |
19 | public init(name: String, alias: String? = nil, mbid: String, gid: Int? = nil, imageUrl: String? = nil, songList: [Titles]? = nil) {
20 | self.name = name
21 | self.alias = alias
22 | self.mbid = mbid
23 | self.gid = gid
24 | self.imageUrl = imageUrl
25 | self.songList = songList
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/Projects/Core/Sources/Model/ArtistInfoModel/SaveArtistInfo.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ArtistInfoModel.swift
3 | // Core
4 | //
5 | // Created by A_Mcflurry on 10/22/23.
6 | // Copyright © 2023 com.creative8.seta. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import SwiftData
11 |
12 | public struct SaveArtistInfo: Codable, Hashable {
13 | public var name: String
14 | public var country: String
15 | public var alias: String
16 | public var mbid: String
17 | public var gid: Int
18 | public var imageUrl: String
19 | public var songList: [Titles]
20 |
21 | public init(name: String, country: String, alias: String, mbid: String, gid: Int, imageUrl: String, songList: [Titles]) {
22 | self.name = name
23 | self.country = country
24 | self.alias = alias
25 | self.mbid = mbid
26 | self.gid = gid
27 | self.imageUrl = imageUrl
28 | self.songList = songList
29 | }
30 | }
31 |
32 | public struct Titles: Codable, Hashable {
33 | public var title: String
34 | public var subTitle: String
35 |
36 | public init(title: String, subTitle: String) {
37 | self.title = title
38 | self.subTitle = subTitle
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/Projects/Core/Sources/Model/ArtistListModel.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ArtistListModel.swift
3 | // Core
4 | //
5 | // Created by 고혜지 on 10/9/23.
6 | // Copyright © 2023 com.creative8. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | // MARK: - ArtistListModel
12 | public struct ArtistListModel: Codable {
13 | public let created: String?
14 | public let count: Int?
15 | public let offset: Int?
16 | public let artists: [MusicBrainzArtist]?
17 | }
18 |
19 | // MARK: - Artist
20 | public struct MusicBrainzArtist: Codable {
21 | public let id: String?
22 | public let name: String?
23 | public let sortName: String?
24 | public let country: String?
25 | public let area: Area?
26 | public let beginArea: Area?
27 | public let aliases: [Alias]?
28 | }
29 |
30 | // MARK: - Alias
31 | public struct Alias: Codable {
32 | public let sortName: String?
33 | public let typeID: String?
34 | public let name: String?
35 | public let locale: String?
36 | public let type: String?
37 | public let primary: Bool?
38 | }
39 |
40 | // MARK: - Area
41 | public struct Area: Codable {
42 | public let id: String?
43 | public let type: String?
44 | public let typeID: String?
45 | public let name: String?
46 | public let sortName: String?
47 | }
48 |
--------------------------------------------------------------------------------
/Projects/Core/Sources/Model/GeniusArtistsModel.swift:
--------------------------------------------------------------------------------
1 | //
2 | // GeniusArtistsModel.swift
3 | // Core
4 | //
5 | // Created by 고혜지 on 10/9/23.
6 | // Copyright © 2023 com.creative8. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | // MARK: - GeniusArtistsModel
12 | public struct GeniusArtistsModel: Codable {
13 | public let response: ArtistsResponse?
14 | }
15 |
16 | // MARK: - Response
17 | public struct ArtistsResponse: Codable {
18 | public let hits: [Hit]?
19 | }
20 |
21 | // MARK: - Hit
22 | public struct Hit: Codable {
23 | public let result: Result?
24 | }
25 |
26 | // MARK: - Result
27 | public struct Result: Codable {
28 | public let artistNames: String?
29 | public let headerImageThumbnailURL: String?
30 | public let headerImageURL: String?
31 | public let songArtImageThumbnailURL: String?
32 | public let songArtImageURL: String?
33 | public let primaryArtist: PrimaryArtist?
34 |
35 | enum CodingKeys: String, CodingKey {
36 | case artistNames = "artist_names"
37 | case headerImageThumbnailURL = "header_image_thumbnail_url"
38 | case headerImageURL = "header_image_url"
39 | case songArtImageThumbnailURL = "song_art_image_thumbnail_url"
40 | case songArtImageURL = "song_art_image_url"
41 | case primaryArtist = "primary_artist"
42 | }
43 | }
44 |
45 | // MARK: - PrimaryArtist
46 | public struct PrimaryArtist: Codable {
47 | public let headerImageURL: String?
48 | public let id: Int?
49 | public let imageURL: String?
50 | public let name: String?
51 |
52 | enum CodingKeys: String, CodingKey {
53 | case headerImageURL = "header_image_url"
54 | case id
55 | case imageURL = "image_url"
56 | case name
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/Projects/Core/Sources/Model/GeniusSongsModel.swift:
--------------------------------------------------------------------------------
1 | //
2 | // GeniusSongsModel.swift
3 | // Core
4 | //
5 | // Created by 고혜지 on 10/9/23.
6 | // Copyright © 2023 com.creative8. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | // MARK: - GeniusSongsModel
12 | public struct GeniusSongsModel: Codable {
13 | public let response: SongsResponse?
14 | }
15 |
16 | // MARK: - Response
17 | public struct SongsResponse: Codable {
18 | public let songs: [GeniusSong]?
19 | public let nextPage: Int?
20 |
21 | enum CodingKeys: String, CodingKey {
22 | case songs
23 | case nextPage = "next_page"
24 | }
25 | }
26 |
27 | // MARK: - Song
28 | public struct GeniusSong: Codable {
29 | public let headerImageThumbnailURL: String?
30 | public let headerImageURL: String?
31 | public let id: Int?
32 | public let songArtImageThumbnailURL: String?
33 | public let songArtImageURL: String?
34 | public let title, titleWithFeatured: String?
35 |
36 | enum CodingKeys: String, CodingKey {
37 | case headerImageThumbnailURL = "header_image_thumbnail_url"
38 | case headerImageURL = "header_image_url"
39 | case id
40 | case songArtImageThumbnailURL = "song_art_image_thumbnail_url"
41 | case songArtImageURL = "song_art_image_url"
42 | case title
43 | case titleWithFeatured = "title_with_featured"
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/Projects/Core/Sources/Model/LikeArtist/LikeArtistModel.swift:
--------------------------------------------------------------------------------
1 | //
2 | // LikeArtistModel.swift
3 | // Feature
4 | //
5 | // Created by A_Mcflurry on 10/17/23.
6 | // Copyright © 2023 com.creative8. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import SwiftData
11 |
12 | @Model
13 | public final class LikeArtist {
14 | public var id: UUID?
15 | public var artistInfo: SaveArtistInfo = SaveArtistInfo(name: "", country: "", alias: "", mbid: "", gid: 0, imageUrl: "", songList: [])
16 | public var orderIndex: Int = 0
17 |
18 | public init(artistInfo: SaveArtistInfo, orderIndex: Int) {
19 | self.id = UUID()
20 | self.artistInfo = artistInfo
21 | self.orderIndex = orderIndex
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/Projects/Core/Sources/Model/OnboardingModel.swift:
--------------------------------------------------------------------------------
1 | //
2 | // OnboardingModel.swift
3 | // Core
4 | //
5 | // Created by 최효원 on 11/2/23.
6 | // Copyright © 2023 com.creative8.seta. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | public struct OnboardingModel: Decodable, Hashable {
12 | public var name: String
13 | public var mbid: String
14 | public var alias: String
15 | public var url: String?
16 | public var gid: Int
17 | public var country: String
18 | public var tags: [String]?
19 | }
20 |
21 | public enum OnboardingFilterType: LocalizedStringResource, CaseIterable {
22 | case all = "전체" // 전체
23 | case kpop = "케이팝" // 케이팝
24 | case pop = "팝" // 팝
25 | case hiphop = "힙합" // 힙합
26 | case rnb = "알앤비" // 알앤비
27 | case rock = "락" // 락
28 | case metal = "메탈" // 메탈
29 | case elctronic = "일렉트로닉" // 일렉트로닉
30 | case folk = "포크" // 포크
31 | case jpop = "제이팝" // 제이팝
32 | }
33 |
--------------------------------------------------------------------------------
/Projects/Core/Sources/Model/SearchHistory/SearchHistoryModel.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SearchHistoryModel.swift
3 | // Core
4 | //
5 | // Created by A_Mcflurry on 10/10/23.
6 | // Copyright © 2023 com.creative8. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import SwiftData
11 |
12 | @Model
13 | public final class SearchHistory {
14 | public var id: UUID?
15 | public var artistInfo: SaveArtistInfo = SaveArtistInfo(name: "", country: "", alias: "", mbid: "", gid: 0, imageUrl: "", songList: [])
16 | public var createdDate: Date = Date()
17 |
18 | init(artistInfo: SaveArtistInfo) {
19 | self.id = UUID()
20 | self.artistInfo = artistInfo
21 | self.createdDate = Date()
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/Projects/Core/Sources/Model/SearchModel.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SearchModel.swift
3 | // Core
4 | //
5 | // Created by 최효원 on 11/8/23.
6 | // Copyright © 2023 com.creative8.seta. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | public let koreanArtistModel: [ArtistInfo] = [
12 | ArtistInfo(name: "BTS", mbid: "0d79fe8e-ba27-4859-bb8c-2f255f346853", imageUrl: "BTS"),
13 | ArtistInfo(name: "NewJeans", mbid: "49204a7a-ed85-407a-828f-6fd46f1d8126", imageUrl: "NewJeans"),
14 | ArtistInfo(name: "Jay Park", mbid: "3dda8202-ce15-4031-862a-77bc6759d15e", imageUrl: "JayPark"),
15 | ArtistInfo(name: "DAY6", mbid: "77191d5f-1045-4b3b-ad7f-12ea398db929", imageUrl: "DAY6"),
16 | ArtistInfo(name: "카더가든", mbid: "9f2862c0-22a9-4fd2-9f1c-07e17f356137", imageUrl: "CarTheGarden"),
17 | ArtistInfo(name: "Paul Kim", mbid: "8968d1ad-c830-4ab2-92d2-bd6bea0b6f42", imageUrl: "PaulKim"),
18 | ArtistInfo(name: "Stray Kids", mbid: "142b343d-bf5a-428c-a64f-6d1a7566bbe9", imageUrl: "StrayKids"),
19 | ArtistInfo(name: "NCT DREAM", mbid: "dc7c8277-d4b6-4bca-bdfe-50eee42536ac", imageUrl: "NCTDream"),
20 | ArtistInfo(name: "이영지", mbid: "0433e75d-70b6-4118-9ff8-9b6f7e534093", imageUrl: "LeeYoungJi"),
21 | ArtistInfo(name: "윤하", mbid: "5e3bc4c7-adbe-40e0-b56e-57d755908d52", imageUrl: "Yoonha")
22 | ]
23 |
24 | public let foreignArtistModel: [ArtistInfo] = [
25 | ArtistInfo(name: "Bruno Mars", mbid: "afb680f2-b6eb-4cd7-a70b-a63b25c763d5", imageUrl: "BrunoMars"),
26 | ArtistInfo(name: "The Weekend", mbid: "c8b03190-306c-4120-bb0b-6f2ebfc06ea9", imageUrl: "TheWeekend"),
27 | ArtistInfo(name: "Maroon5", mbid: "0ab49580-c84f-44d4-875f-d83760ea2cfe", imageUrl: "Maroon5"),
28 | ArtistInfo(name: "Post Malone", mbid: "b1e26560-60e5-4236-bbdb-9aa5a8d5ee19", imageUrl: "PostMalone"),
29 | ArtistInfo(name: "Sam Smith", mbid: "5a85c140-dcf9-4dd2-b2c8-aff0471549f3", imageUrl: "SamSmith"),
30 | ArtistInfo(name: "Doja Cat", mbid: "5df62a88-cac9-490a-b62c-c7c88f4020f4", imageUrl: "DojaCat"),
31 | ArtistInfo(name: "Eminem", mbid: "b95ce3ff-3d05-4e87-9e01-c97b66af13d4", imageUrl: "Eminem"),
32 | ArtistInfo(name: "Dua Lipa", mbid: "6f1a58bf-9b1b-49cf-a44a-6cefad7ae04f", imageUrl: "DuaLipa"),
33 | ArtistInfo(name: "John K", mbid: "be465d46-9615-4722-b828-ea531ff97ea3", imageUrl: "JohnK"),
34 | ArtistInfo(name: "Troye Sivan", mbid: "e5712ceb-c37a-4c49-a11c-ccf4e21852d4", imageUrl: "TroyeSivan"),
35 | ArtistInfo(name: "Drake", mbid: "9fff2f8a-21e6-47de-a2b8-7f449929d43f", imageUrl: "Drake")
36 |
37 | ]
38 |
--------------------------------------------------------------------------------
/Projects/Core/Sources/Model/SetlistListModel.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SetlistListModel.swift
3 | // Core
4 | //
5 | // Created by 고혜지 on 10/9/23.
6 | // Copyright © 2023 com.creative8. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | // MARK: - SetlistListModel
12 | public struct SetlistListModel: Codable {
13 | public let type: String?
14 | public let itemsPerPage: Int?
15 | public let page: Int?
16 | public let total: Int?
17 | public let setlist: [Setlist]?
18 | }
19 |
20 | // MARK: - Setlist
21 | public struct Setlist: Codable {
22 | public let id: String?
23 | public let versionId: String?
24 | public let eventDate: String?
25 | public let lastUpdated: String?
26 | public let artist: Artist?
27 | public let venue: Venue?
28 | public let sets: Sets?
29 | public let url: String?
30 | public let info: String?
31 | public let tour: Tour?
32 |
33 | public init(id: String?, versionId: String?, eventDate: String?, lastUpdated: String?, artist: Artist?, venue: Venue?, sets: Sets?, url: String?, info: String?, tour: Tour?) {
34 | self.id = id
35 | self.versionId = versionId
36 | self.eventDate = eventDate
37 | self.lastUpdated = lastUpdated
38 | self.artist = artist
39 | self.venue = venue
40 | self.sets = sets
41 | self.url = url
42 | self.info = info
43 | self.tour = tour
44 | }
45 | }
46 |
47 | // MARK: - Artist
48 | public struct Artist: Codable, Hashable, Equatable {
49 | public let mbid: String?
50 | public let name: String?
51 | public let sortName: String?
52 | public let disambiguation: String?
53 | public let url: String?
54 |
55 | public init(mbid: String?, name: String?, sortName: String?, disambiguation: String?, url: String?) {
56 | self.mbid = mbid
57 | self.name = name
58 | self.sortName = sortName
59 | self.disambiguation = disambiguation
60 | self.url = url
61 | }
62 | }
63 |
64 | // MARK: - Sets
65 | public struct Sets: Codable {
66 | public let setsSet: [Session]?
67 |
68 | public init(setsSet: [Session]?) {
69 | self.setsSet = setsSet
70 | }
71 |
72 | enum CodingKeys: String, CodingKey {
73 | case setsSet = "set"
74 | }
75 | }
76 |
77 | // MARK: - Session
78 | public struct Session: Codable, Hashable {
79 | public let song: [Song]?
80 | public let encore: Int?
81 | public let name: String?
82 |
83 | public init(song: [Song]?, encore: Int?, name: String?) {
84 | self.song = song
85 | self.encore = encore
86 | self.name = name
87 | }
88 | }
89 |
90 | extension Session: Equatable {
91 | public static func == (lhs: Session, rhs: Session) -> Bool {
92 | return lhs.song == rhs.song &&
93 | lhs.encore == rhs.encore &&
94 | lhs.name == rhs.name
95 | }
96 | }
97 |
98 | // MARK: - Song
99 | public struct Song: Codable, Hashable, Equatable {
100 | public static func == (lhs: Song, rhs: Song) -> Bool {
101 | return lhs.name == rhs.name && lhs.info == rhs.info && lhs.cover == rhs.cover && lhs.tape == rhs.tape
102 | }
103 |
104 | public let name: String?
105 | public let info: String?
106 | public let cover: Artist?
107 | public let tape: Bool?
108 | }
109 |
110 | // MARK: - Tour
111 | public struct Tour: Codable {
112 | public let name: String?
113 | }
114 |
115 | // MARK: - Venue
116 | public struct Venue: Codable {
117 | public let id: String?
118 | public let name: String?
119 | public let city: City?
120 | public let url: String?
121 | }
122 |
123 | // MARK: - City
124 | public struct City: Codable {
125 | public let id: String?
126 | public let name: String?
127 | public let state: String?
128 | public let stateCode: String?
129 | public let coords: Coords?
130 | public let country: Country?
131 | }
132 |
133 | // MARK: - Coords
134 | public struct Coords: Codable {
135 | public let lat: Double?
136 | public let long: Double?
137 | }
138 |
139 | // MARK: - Country
140 | public struct Country: Codable {
141 | public let code: String?
142 | public let name: String?
143 | }
144 |
--------------------------------------------------------------------------------
/Projects/Core/Sources/Service/APIKeys.swift:
--------------------------------------------------------------------------------
1 | //
2 | // APIKeys.swift
3 | // Core
4 | //
5 | // Created by 고혜지 on 10/12/23.
6 | // Copyright © 2023 com.creative8. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | struct APIKeys {
12 | let setlistFM = "YOUR_API_KEY"
13 | let musicBrainz = "YOUR_API_KEY"
14 | let genius = "YOUR_API_KEY"
15 | }
16 |
--------------------------------------------------------------------------------
/Projects/Core/Sources/Service/AppState.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AppState.swift
3 | // Core
4 | //
5 | // Created by 예슬 on 3/7/25.
6 | // Copyright © 2025 com.creative8.seta. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | public class AppState: ObservableObject {
12 | @Published public var isOnboarding: Bool {
13 | didSet {
14 | UserDefaults.standard.set(isOnboarding, forKey: "isOnboarding")
15 | }
16 | }
17 |
18 | public init() {
19 | if UserDefaults.standard.object(forKey: "isOnboarding") == nil {
20 | self.isOnboarding = true
21 | UserDefaults.standard.set(true, forKey: "isOnboarding")
22 | } else {
23 | self.isOnboarding = UserDefaults.standard.bool(forKey: "isOnboarding")
24 | }
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/Projects/Core/Sources/Service/AppleMusicService.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AppleMusicService.swift
3 | // Core
4 | //
5 | // Created by 최효원 on 10/18/23.
6 | // Copyright © 2023 com.creative8.seta. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import MusicKit
11 | import StoreKit
12 | import Combine
13 |
14 | public final class AppleMusicService: MusicPlaylistAddable, Sendable {
15 | public var id: MusicItemID = ""
16 |
17 | public init() {
18 | }
19 |
20 | actor SearchResultsManager {
21 | private var results: [(Int, MusicCatalogSearchResponse?)] = []
22 |
23 | func append(_ result: (Int, MusicCatalogSearchResponse?)) {
24 | results.append(result)
25 | }
26 |
27 | func getSortedResults() -> [(Int, MusicCatalogSearchResponse?)] {
28 | return results.sorted { $0.0 < $1.0 }
29 | }
30 | }
31 |
32 | public func addPlayList(name: String, musicList: [(String, String?)], venue: String?) {
33 | let searchResultsManager = SearchResultsManager()
34 |
35 | Task {
36 | // musicList를 index가 들어간 튜플로 변환
37 | let indexedMusicList = musicList.enumerated().map { (index, music) in
38 | (index, music)
39 | }
40 |
41 | // 모든 음악을 비동기로 검색
42 | await withTaskGroup(of: Void.self) { group in
43 | for (index, music) in indexedMusicList {
44 | group.addTask {
45 |
46 | do {
47 | let response = try await self.searchMusic(title: music.0, singer: music.1 ?? "")
48 |
49 | // actor를 통해 안전하게 searchResults에 추가
50 | await searchResultsManager.append((index, response))
51 | } catch {
52 | print("Error searching for '\(music.0)': \(error)")
53 |
54 | // 실패한 경우에도 인덱스를 유지
55 | await searchResultsManager.append((index, nil))
56 | }
57 | }
58 | }
59 | }
60 |
61 | // 검색 결과를 인덱스 순서대로 정렬
62 | let sortedResults = await searchResultsManager.getSortedResults()
63 |
64 | // 유효한 곡만 필터링하여 배열로 변환
65 | let songsToAdd = sortedResults.compactMap { $0.1?.songs.first }
66 |
67 | // 플레이리스트 생성 및 곡 추가를 더 빠르게 처리
68 | let _ = try await MusicLibrary.shared.createPlaylist(
69 | name: name,
70 | description: "Seta에서 생성된 플레이리스트 입니다.",
71 | authorDisplayName: venue,
72 | items: songsToAdd
73 | )
74 | }
75 | }
76 |
77 | // MusicCatalogSearchRequest를 비동기로 처리하는 함수
78 | private func searchMusic(title: String, singer: String) async throws -> MusicCatalogSearchResponse {
79 | var request = MusicCatalogSearchRequest(term: "\(singer) \(title)", types: [MusicKit.Song.self])
80 | request.includeTopResults = true
81 | let response = try await request.response()
82 | return response
83 | }
84 |
85 | public func requestMusicAuthorization() {
86 | SKCloudServiceController.requestAuthorization { [weak self] status in
87 | switch status {
88 | case .authorized:
89 | print("Apple Music authorized")
90 | case .restricted:
91 | print("Apple Music authorization restricted")
92 | case .notDetermined:
93 | print("Apple Music authorization not determined")
94 | case .denied:
95 | print("Apple Music authorization denied")
96 | @unknown default:
97 | print("")
98 | }
99 | }
100 | }
101 |
102 | }
103 |
104 | public final class CheckAppleMusicSubscription: ObservableObject {
105 | @Published var check: Bool = false
106 | public static let shared: CheckAppleMusicSubscription = CheckAppleMusicSubscription()
107 | // 사용자가 애플 뮤직을 구독 중인지 확인
108 | public init() {
109 | }
110 |
111 | public func appleMusicSubscription(completion: @escaping (Bool) -> Void) {
112 | SKCloudServiceController().requestCapabilities { (capability: SKCloudServiceCapability, err: Error?) in
113 | guard err == nil else { return }
114 | if capability.contains(SKCloudServiceCapability.musicCatalogPlayback) {
115 | self.check = true
116 | } else if capability.contains(SKCloudServiceCapability.musicCatalogSubscriptionEligible) {
117 | self.check = false
118 | }
119 | completion(self.check)
120 | }
121 | }
122 | }
123 |
--------------------------------------------------------------------------------
/Projects/Core/Sources/Service/ArtistFetchService.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ArtistFetchService.swift
3 | // Core
4 | //
5 | // Created by A_Mcflurry on 11/25/23.
6 | // Copyright © 2023 com.creative8.seta. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | public final class ArtistFetchService: ObservableObject {
12 | public init() { }
13 |
14 | @Published public var allArtist: [OnboardingModel] = []
15 |
16 | // 주 요청 URL을 새로 받은 URL로 변경
17 | private let urls: [URL] = [
18 | URL(string: "https://port-0-seta-server-bkcl2bloxy1ug8.sel5.cloudtype.app/api/getArtists")!,
19 | URL(string: "https://seta-server.fly.dev/api/getArtists")!
20 | ]
21 |
22 | public func fetchData(completion: @escaping (Bool) -> Void) {
23 | fetch(at: 0, completion: completion)
24 | }
25 |
26 | private func fetch(at index: Int, completion: @escaping (Bool) -> Void) {
27 | guard index < urls.count else {
28 | completion(false)
29 | return
30 | }
31 |
32 | var request: URLRequest = URLRequest(url: urls[index], timeoutInterval: 5)
33 |
34 | URLSession.shared.dataTask(with: request) { [weak self] data, _, error in
35 | guard let self else { return }
36 |
37 | guard let data,
38 | let artists = try? JSONDecoder().decode([OnboardingModel].self, from: data) else { fetch(at: index + 1, completion: completion); return }
39 |
40 | Task { @MainActor in
41 | self.allArtist = artists
42 | completion(true)
43 | }
44 | }.resume()
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/Projects/Core/Sources/Service/NetworkManager.swift:
--------------------------------------------------------------------------------
1 | //
2 | // NetworkManager.swift
3 | // Core
4 | //
5 | // Created by 장수민 on 2/26/24.
6 | // Copyright © 2024 com.creative8.seta. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import Network
11 |
12 | @Observable
13 | public final class NetworkMonitor: ObservableObject {
14 | public let networkMonitor = NWPathMonitor()
15 | public let queue = DispatchQueue(label: "Monitor")
16 | public var isConnected = false
17 |
18 | public init() {
19 | networkMonitor.pathUpdateHandler = { path in
20 | self.isConnected = path.status == .satisfied
21 | }
22 | networkMonitor.start(queue: queue)
23 | }
24 |
25 | public func startMonitoring() {
26 | networkMonitor.start(queue: queue)
27 | networkMonitor.pathUpdateHandler = { path in
28 | self.isConnected = path.status == .satisfied
29 |
30 | if path.usesInterfaceType(.wifi) {
31 | print("Using wifi")
32 | } else if path.usesInterfaceType(.cellular) {
33 | print("Using cellular")
34 | }
35 | }
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/Projects/Core/Sources/Service/SearchHistoryDataManager/KoreanConverter.swift:
--------------------------------------------------------------------------------
1 | //
2 | // KoreanConverter.swift
3 | // Core
4 | //
5 | // Created by 고혜지 on 10/18/23.
6 | // Copyright © 2023 com.creative8. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | public final class KoreanConverter {
12 | public init() {
13 |
14 | }
15 |
16 | public func isKorean() -> Bool {
17 | guard let languageCode = Locale.current.language.languageCode?.identifier else { return false }
18 | return languageCode == "ko"
19 | }
20 |
21 | public func findKoreanName(artist: MusicBrainzArtist) -> (String, String?) {
22 | var primaryAlias: String?
23 |
24 | guard let aliases = artist.aliases else { return (artist.name!, nil) }
25 |
26 | if self.isKorean() {
27 | for alias in aliases {
28 | if ((alias.locale == "ko" || alias.locale == "ko_KR") && (alias.name?.lowercased() != artist.name?.lowercased())) {
29 | primaryAlias = alias.name
30 | return (artist.name!, primaryAlias)
31 | }
32 | }
33 | }
34 |
35 | for alias in aliases {
36 | if (alias.primary == true) && (alias.name?.lowercased() != artist.name?.lowercased()) {
37 | primaryAlias = alias.name
38 | return (artist.name!, primaryAlias)
39 | }
40 | }
41 |
42 | if let alias = artist.aliases?[0] {
43 | primaryAlias = alias.name
44 | }
45 |
46 | return (artist.name!, primaryAlias)
47 | }
48 |
49 | public func findKoreanTitle(title: String, songList: [Titles]) -> String? {
50 | for song in songList {
51 | if title.lowercased() == song.title.lowercased() {
52 | return title
53 | } else if title.lowercased() == song.subTitle.lowercased() {
54 | return song.title
55 | }
56 | }
57 |
58 | for song in songList {
59 | if compareSongTitles(title.lowercased(), song.subTitle.lowercased()) >= 0.6 {
60 | return song.title
61 | }
62 | }
63 |
64 | return nil
65 | }
66 |
67 | // Levenshtein Distance를 계산하는 함수
68 | private func levenshteinDistance(_ s1: String, _ s2: String) -> Int {
69 | let len1 = s1.count
70 | let len2 = s2.count
71 | var matrix = Array(repeating: Array(repeating: 0, count: len2 + 1), count: len1 + 1)
72 |
73 | for rowIndex in 0...len1 {
74 | matrix[rowIndex][0] = rowIndex
75 | }
76 |
77 | for colIndex in 0...len2 {
78 | matrix[0][colIndex] = colIndex
79 | }
80 |
81 | for (rowIndex, char1) in s1.enumerated() {
82 | for (colIndex, char2) in s2.enumerated() {
83 | let cost = char1 == char2 ? 0 : 1
84 | matrix[rowIndex + 1][colIndex + 1] = min(
85 | matrix[rowIndex][colIndex + 1] + 1,
86 | matrix[rowIndex + 1][colIndex] + 1,
87 | matrix[rowIndex][colIndex] + cost
88 | )
89 | }
90 | }
91 |
92 | return matrix[len1][len2]
93 | }
94 |
95 | // 노래 제목 유사도 비교 함수
96 | private func compareSongTitles(_ title1: String, _ title2: String) -> Double {
97 | let distance = levenshteinDistance(title1, title2)
98 | let maxLength = max(title1.count, title2.count)
99 | return 1.0 - Double(distance) / Double(maxLength)
100 | }
101 | }
102 |
--------------------------------------------------------------------------------
/Projects/Core/Sources/Service/SetlistDataService.swift:
--------------------------------------------------------------------------------
1 | //
2 | // DataService.swift
3 | // Core
4 | //
5 | // Created by 고혜지 on 10/9/23.
6 | // Copyright © 2023 com.creative8. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | public final class SetlistDataService {
12 | public init() {
13 |
14 | }
15 |
16 | private func APIRequest(url: URL, httpMethod: String, headers: [String: String], completion: @escaping (T?) -> Void) {
17 | var request = URLRequest(url: url)
18 | request.httpMethod = httpMethod
19 |
20 | for (headerField, headerValue) in headers {
21 | request.setValue(headerValue, forHTTPHeaderField: headerField)
22 | }
23 |
24 | let session = URLSession(configuration: .default)
25 |
26 | let task = session.dataTask(with: request) { data, _, error in
27 | if let error = error {
28 | print(error)
29 | completion(nil)
30 | } else if let JSONdata = data {
31 | let decoder = JSONDecoder()
32 | do {
33 | let decodedData = try decoder.decode(T.self, from: JSONdata)
34 | completion(decodedData)
35 | } catch let error {
36 | print(error)
37 | completion(nil)
38 | }
39 | } else {
40 | completion(nil)
41 | }
42 | }
43 | task.resume()
44 | }
45 |
46 | public func fetchArtistFromMusicBrainz(artistMbid: String, completion: @escaping (ArtistListModel?) -> Void) {
47 | if let url = URL(string: "https://musicbrainz.org/ws/2/artist/?query=mbid:\(artistMbid)&fmt=json") {
48 | let headers = ["Authorization": APIKeys().musicBrainz]
49 | APIRequest(url: url, httpMethod: "GET", headers: headers, completion: completion)
50 | } else {
51 | completion(nil)
52 | }
53 | }
54 |
55 | public func searchArtistsFromMusicBrainz(artistName: String, completion: @escaping (ArtistListModel?) -> Void) {
56 | if let url = URL(string: "https://musicbrainz.org/ws/2/artist?query=\(artistName)&fmt=json") {
57 | let headers = ["Authorization": APIKeys().musicBrainz]
58 | APIRequest(url: url, httpMethod: "GET", headers: headers, completion: completion)
59 | } else {
60 | completion(nil)
61 | }
62 | }
63 |
64 | public func fetchOneSetlistFromSetlistFM(setlistId: String, completion: @escaping (Setlist?) -> Void) {
65 | if let url = URL(string: "https://api.setlist.fm/rest/1.0/setlist/\(setlistId)") {
66 | let headers = [
67 | "x-api-key": APIKeys().setlistFM,
68 | "Accept": "application/json",
69 | "Accept-Language": "en"
70 | ]
71 | APIRequest(url: url, httpMethod: "GET", headers: headers, completion: completion)
72 | } else {
73 | completion(nil)
74 | }
75 | }
76 |
77 | public func fetchSetlistsFromSetlistFM(artistMbid: String, page: Int, completion: @escaping (SetlistListModel?) -> Void) {
78 | if let url = URL(string: "https://api.setlist.fm/rest/1.0/search/setlists?artistMbid=\(artistMbid)&p=\(page)") {
79 | let headers = [
80 | "x-api-key": APIKeys().setlistFM,
81 | "Accept": "application/json",
82 | "Accept-Language": "en"
83 | ]
84 | APIRequest(url: url, httpMethod: "GET", headers: headers, completion: completion)
85 | } else {
86 | completion(nil)
87 | }
88 | }
89 |
90 | public func searchArtistFromGenius(artistName: String, completion: @escaping (GeniusArtistsModel?) -> Void) {
91 | if let url = URL(string: "https://api.genius.com/search?q=\(artistName)") {
92 | print("request url: \(url)")
93 | let headers = ["Authorization": APIKeys().genius]
94 | APIRequest(url: url, httpMethod: "GET", headers: headers, completion: completion)
95 | } else {
96 | completion(nil)
97 | }
98 | }
99 |
100 | public func fetchSongsFromGenius(artistId: Int, page: Int, completion: @escaping (GeniusSongsModel?) -> Void) {
101 | if let url = URL(string: "https://api.genius.com/artists/\(artistId)/songs?page=\(page)&per_page=50") {
102 | let headers = ["Authorization": APIKeys().genius]
103 | APIRequest(url: url, httpMethod: "GET", headers: headers, completion: completion)
104 | } else {
105 | completion(nil)
106 | }
107 | }
108 |
109 | }
110 |
--------------------------------------------------------------------------------
/Projects/Core/Sources/Service/UserDefaultsManger.swift:
--------------------------------------------------------------------------------
1 | //
2 | // UserDefaultsManger.swift
3 | // Core
4 | //
5 | // Created by A_Mcflurry on 6/30/24.
6 | // Copyright © 2024 com.creative8.seta. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | public final class UserDefaultsManger {
12 | enum UserDefaultsKey: String {
13 | case accessToken
14 | }
15 |
16 | public var accessToken: String {
17 | get {
18 | UserDefaults.standard.string(forKey: UserDefaultsKey.accessToken.rawValue) ?? ""
19 | } set {
20 | UserDefaults.standard.setValue(newValue, forKey: UserDefaultsKey.accessToken.rawValue)
21 | }
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/Projects/Feature/Feature.xcodeproj/xcuserdata/a_mcflurry.xcuserdatad/xcschemes/xcschememanagement.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | SchemeUserState
6 |
7 | Feature.xcscheme_^#shared#^_
8 |
9 | isShown
10 |
11 | orderHint
12 | 1
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/Projects/Feature/Feature.xcodeproj/xcuserdata/kohyeji.xcuserdatad/xcschemes/xcschememanagement.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | SchemeUserState
6 |
7 | Feature.xcscheme_^#shared#^_
8 |
9 | isShown
10 |
11 | orderHint
12 | 1
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/Projects/Feature/Feature.xcodeproj/xcuserdata/yeseul.xcuserdatad/xcschemes/xcschememanagement.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | SchemeUserState
6 |
7 | Feature.xcscheme_^#shared#^_
8 |
9 | isShown
10 |
11 | orderHint
12 | 1
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/Projects/Feature/Project.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Project.swift
3 | // ProjectDescriptionHelpers
4 | //
5 | // Created by 최효원 on 2023/10/06.
6 | //
7 |
8 | import ProjectDescription
9 | import ProjectDescriptionHelpers
10 |
11 | let project = Project.makeModule(
12 | name: "Feature",
13 | product: .staticFramework,
14 | packages: [
15 | .Firebase,
16 | .MarqueeText
17 | ],
18 | dependencies: [
19 | .project(target: "Core", path: .relativeToRoot("Projects/Core")),
20 | .project(target: "UI", path: .relativeToRoot("Projects/UI")),
21 | .SPM.MarqueeText,
22 | .SPM.Firebase
23 | ],
24 | sources: ["Scenes/**"]
25 | )
26 |
--------------------------------------------------------------------------------
/Projects/Feature/Scenes/ArchiveScene/Component/ArchiveArtistCell.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ArchiveArtistCell.swift
3 | // Feature
4 | //
5 | // Created by A_Mcflurry on 10/14/23.
6 | // Copyright © 2023 com.creative8. All rights reserved.
7 | //
8 |
9 | import SwiftUI
10 | import UI
11 |
12 | struct ArchiveArtistCellImage: View {
13 | let artistUrl: URL?
14 | @Environment(\.colorScheme) var colorScheme
15 | var body: some View {
16 | Group {
17 | if let url = artistUrl {
18 | AsyncImage(url: url) { image in
19 | image
20 | .centerCropped()
21 | } placeholder: {
22 | ProgressView()
23 | }
24 | } else {
25 | if colorScheme == .light {
26 | Image(uiImage: UIImage(named: "artistViewTicket", in: Bundle(identifier: "com.creative8.seta.UI"), compatibleWith: nil)!)
27 | .centerCropped()
28 | .overlay(
29 | RoundedRectangle(cornerRadius: 12)
30 | .stroke(Color.gray, lineWidth: 1) // 색상과 선 두께를 원하는 대로 설정
31 | )
32 | } else {
33 | Image(uiImage: UIImage(named: "darkArtistViewTicket", in: Bundle(identifier: "com.creative8.seta.UI"), compatibleWith: nil)!)
34 | .centerCropped()
35 | .overlay(
36 | RoundedRectangle(cornerRadius: 12)
37 | .stroke(Color.gray, lineWidth: 1) // 색상과 선 두께를 원하는 대로 설정
38 | )
39 | }
40 | }
41 | }
42 | .aspectRatio(1.0, contentMode: .fit)
43 | .clipShape(RoundedRectangle(cornerRadius: 12))
44 | .frameForCell()
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/Projects/Feature/Scenes/ArchiveScene/Component/ArtistSetCell.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ArtistSetCell.swift
3 | // Feature
4 | //
5 | // Created by A_Mcflurry on 10/31/23.
6 | // Copyright © 2023 com.creative8.seta. All rights reserved.
7 | //
8 |
9 | import SwiftUI
10 | import UI
11 |
12 | struct ArtistSetCell: View {
13 | let name: String
14 | let artistImgUrl: URL?
15 | let isSelected: Bool
16 | @Environment(\.colorScheme) var colorScheme
17 |
18 | var body: some View {
19 | HStack {
20 | artistImg
21 | Text(name)
22 | .font(.subheadline)
23 | .fontWeight(isSelected ? .semibold : .regular)
24 | .foregroundStyle(isSelected ? Color.mainWhite : Color.mainBlack)
25 | .frame(maxWidth: UIWidth * 0.17)
26 | }
27 | .padding(6)
28 | .background {
29 | let color = isSelected ? Color.mainBlack : Color.mainWhite
30 | color.clipShape(RoundedRectangle(cornerRadius: 12))
31 | }
32 | .lineLimit(1)
33 | }
34 |
35 | @ViewBuilder
36 | private var artistImg: some View {
37 | Group {
38 | if let url = artistImgUrl {
39 | AsyncImage(url: url) { image in
40 | image
41 | .centerCropped()
42 | // .overlay(
43 | // RoundedRectangle(cornerRadius: 8)
44 | //// .stroke(Color.mainGrey1, lineWidth: 1)
45 | // .foregroundStyle(Color.clear)
46 | // )
47 | } placeholder: {
48 | ProgressView()
49 | }
50 | } else {
51 | if colorScheme == .light {
52 | Image(uiImage: UIImage(named: "artistViewTicket", in: Bundle(identifier: "com.creative8.seta.UI"), compatibleWith: nil)!)
53 | .centerCropped()
54 | // .overlay(
55 | // RoundedRectangle(cornerRadius: 8)
56 | // .stroke(Color(UIColor.systemGray), lineWidth: 1) // 색상과 선 두께를 원하는 대로 설정
57 | // )
58 | } else {
59 | Image(uiImage: UIImage(named: "darkArtistViewTicket", in: Bundle(identifier: "com.creative8.seta.UI"), compatibleWith: nil)!)
60 | .centerCropped()
61 | // .overlay(
62 | // RoundedRectangle(cornerRadius: 8)
63 | // .stroke(Color(UIColor.systemGray), lineWidth: 1) // 색상과 선 두께를 원하는 대로 설정
64 | // )
65 | }
66 | }
67 | }
68 | .aspectRatio(1.0, contentMode: .fit)
69 | .clipShape(RoundedRectangle(cornerRadius: 8))
70 | }
71 | }
72 |
73 | struct AllArtistsSetCell: View {
74 | let name: LocalizedStringResource
75 | let isSelected: Bool
76 | var body: some View {
77 | Text(name)
78 | .foregroundStyle(isSelected ? Color.mainWhite : Color.mainBlack)
79 | .fontWeight(isSelected ? .bold : .regular)
80 | .padding(10)
81 | .background {
82 | let color = isSelected ? Color.mainBlack : Color.mainWhite
83 | color.clipShape(RoundedRectangle(cornerRadius: 12))
84 | }
85 | }
86 | }
87 | //
88 | // #Preview {
89 | // ArtistSetCell(name: "방탄소년단", isSelected: false)
90 | // }
91 |
--------------------------------------------------------------------------------
/Projects/Feature/Scenes/ArchiveScene/Component/EmptyArtistImage.swift:
--------------------------------------------------------------------------------
1 | //
2 | // EmptyArtistImage.swift
3 | // Feature
4 | //
5 | // Created by 장수민 on 7/24/24.
6 | // Copyright © 2024 com.creative8.seta. All rights reserved.
7 | //
8 |
9 | import SwiftUI
10 |
11 | struct EmptyArtistImage: View {
12 | let foregroundColor: Color
13 | let backgroundColor: Color
14 | var body: some View {
15 | // TODO: 컬러 수정
16 | HStack(spacing: 0) {
17 | Rectangle()
18 | .foregroundStyle(foregroundColor)
19 |
20 | Rectangle()
21 | .foregroundStyle(foregroundColor)
22 | .cornerRadius(40, corners: [.topLeft])
23 | }
24 | .background(backgroundColor)
25 | }
26 | }
27 |
28 | #Preview {
29 | EmptyArtistImage(foregroundColor: Color.orange, backgroundColor: Color.yellow)
30 | }
31 |
--------------------------------------------------------------------------------
/Projects/Feature/Scenes/ArchiveScene/Component/IsEmptyCell.swift:
--------------------------------------------------------------------------------
1 | //
2 | // IsEmptyCell.swift
3 | // Feature
4 | //
5 | // Created by A_Mcflurry on 11/2/23.
6 | // Copyright © 2023 com.creative8.seta. All rights reserved.
7 | //
8 |
9 | import SwiftUI
10 | import UI
11 |
12 | struct IsEmptyCell: View {
13 | enum EmptyType {
14 | case bookmark
15 | case likeArtist
16 | }
17 | let type: EmptyType
18 | var body: some View {
19 | VStack {
20 | Text(type == .bookmark ? "북마크 한 공연이 없어요" : "찜한 아티스트가 없어요")
21 | .font(.headline)
22 | .foregroundStyle(Color.mainBlack)
23 | .padding(.bottom, 12)
24 | Group {
25 | Text(type == .bookmark ? "관심있는 공연을 북마크하고" : "관심있는 아티스트를 찜하고")
26 | Text(type == .bookmark ? "세트리스트를 바로 확인해보세요" : "공연 및 세트리스트 정보를 빠르게 확인해보세요")
27 | }
28 | .font(.footnote)
29 | .foregroundStyle(Color.gray)
30 | }
31 | .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .center)
32 | .multilineTextAlignment(.center)
33 | }
34 | }
35 |
36 | #Preview {
37 | IsEmptyCell(type: .bookmark)
38 | }
39 |
--------------------------------------------------------------------------------
/Projects/Feature/Scenes/ArchiveScene/Component/MenuButton.swift:
--------------------------------------------------------------------------------
1 | //
2 | // MenuButton.swift
3 | // Feature
4 | //
5 | // Created by A_Mcflurry on 11/1/23.
6 | // Copyright © 2023 com.creative8.seta. All rights reserved.
7 | //
8 |
9 | import SwiftUI
10 | import Core
11 | import UI
12 |
13 | struct MenuButton: View {
14 | @Binding var selectedTab: Tab
15 | let item: LikeArtist
16 | @StateObject var dataManager = SwiftDataManager()
17 | @Environment(\.modelContext) var modelContext
18 | var body: some View {
19 | Menu {
20 | NavigationLink(value: NavigationDelivery(artistInfo: SaveArtistInfo(name: item.artistInfo.name, country: "", alias: item.artistInfo.alias, mbid: item.artistInfo.mbid, gid: 0, imageUrl: "", songList: []))) {
21 | Text("아티스트 보기")
22 | }
23 | Button("찜하기 취소") { dataManager.deleteLikeArtist(item) }
24 | } label: {
25 | Image(systemName: "ellipsis")
26 | .foregroundStyle(Color.mainBlack)
27 | .padding()
28 | .background(Color.clear)
29 | }
30 | .onAppear { dataManager.modelContext = modelContext }
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/Projects/Feature/Scenes/ArchiveScene/View/ArchivingArtistView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ArchivingArtistView.swift
3 | // Feature
4 | //
5 | // Created by 최효원 on 7/31/24.
6 | // Copyright © 2024 com.creative8.seta. All rights reserved.
7 | //
8 |
9 | import SwiftUI
10 | import SwiftData
11 | import Core
12 | import UI
13 | import Combine
14 |
15 | struct ArchivingArtistView: View {
16 | @Query(sort: \LikeArtist.orderIndex, order: .reverse) var likeArtists: [LikeArtist]
17 | @Query(sort: \ArchivedConcertInfo.setlist.date, order: .reverse) var concertInfo: [ArchivedConcertInfo]
18 | @StateObject var viewModel = ArchivingViewModel.shared
19 | @Environment(\.modelContext) var modelContext
20 | @Namespace var topID
21 |
22 | var body: some View {
23 | ZStack {
24 | Color.gray6.ignoresSafeArea()
25 | VStack {
26 | HStack {
27 | Text("찜한 아티스트")
28 | .font(.title).bold()
29 | .foregroundStyle(Color.mainBlack)
30 | .padding([.leading, .top], 23)
31 | Spacer()
32 | }
33 | .padding(.bottom, -5)
34 |
35 | if likeArtists.isEmpty {
36 | IsEmptyCell(type: .likeArtist)
37 | } else {
38 | List {
39 | VStack(alignment: .leading) {
40 | Group {
41 | Text("상단 5명의 아티스트만 홈 화면에 표시됩니다\n")
42 | Text("변경을 원하신다면 순서를 옮겨보세요")
43 | }
44 | .font(.footnote)
45 | .foregroundStyle(Color.gray)
46 | .padding(.bottom, 6)
47 |
48 | }.id(topID)
49 | .listRowSeparator(.hidden)
50 | .listRowBackground(Color.gray6)
51 |
52 | archiveArtistListCell
53 | .listRowSeparator(.hidden)
54 | .listRowBackground(Color.gray6)
55 | }
56 | .padding(.horizontal, 5)
57 | .scrollIndicators(.hidden)
58 | .listStyle(.plain)
59 |
60 | }
61 | }
62 | }
63 | }
64 |
65 | private var archiveArtistListCell: some View {
66 | ForEach(Array(likeArtists.enumerated()), id: \.element) { index, item in
67 | HStack(spacing: 16) {
68 | ArchiveArtistCellImage(artistUrl: URL(string: item.artistInfo.imageUrl))
69 | Text(item.artistInfo.name)
70 | .font(.subheadline)
71 | .foregroundStyle(index < 5 ? Color.mainOrange : Color.mainBlack)
72 | .bold(index < 5)
73 | Spacer()
74 | Image(systemName: "chevron.up.chevron.down")
75 | .foregroundColor(.mainBlack)
76 | }
77 | if index == 4 {
78 | Divider()
79 | .background(Color(UIColor.systemGray3))
80 | }
81 | }
82 | .onMove { source, destination in
83 | var updatedItems = likeArtists
84 | updatedItems.move(fromOffsets: source, toOffset: destination)
85 |
86 | for (index, item) in updatedItems.enumerated() {
87 | item.orderIndex = likeArtists.count - 1 - index
88 | }
89 |
90 | do {
91 | try modelContext.save()
92 | } catch {
93 | print("Failed to save context: \(error)")
94 | }
95 | }
96 | }
97 | }
98 |
--------------------------------------------------------------------------------
/Projects/Feature/Scenes/ArchiveScene/ViewModel/ArchivingViewModel.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ArchivingViewModel.swift
3 | // Feature
4 | //
5 | // Created by A_Mcflurry on 10/31/23.
6 | // Copyright © 2023 com.creative8.seta. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import Core
11 | import Combine
12 | import SwiftUI
13 |
14 | class ArchivingViewModel: ObservableObject {
15 |
16 | // // MARK: Tab View
17 | // @Published var pageStack: [NavigationDelivery] = []
18 | //
19 | // private var subscription: AnyCancellable?
20 | //
21 | // init(consecutiveTaps: AnyPublisher) {
22 | // subscription = consecutiveTaps
23 | // .sink { [weak self] in
24 | // guard let self else { return }
25 | //
26 | // withAnimation {
27 | // if self.pageStack.isEmpty {
28 | // // Scroll 최상단으로 이동
29 | // } else {
30 | // self.pageStack.removeLast()
31 | // }
32 | // }
33 | //
34 | // }
35 | // }
36 |
37 | func getDateFormatted(date: Date) -> String {
38 | guard let languageCode = Locale.current.language.languageCode?.identifier else { return "" }
39 |
40 | let dateFormatter = DateFormatter()
41 |
42 | dateFormatter.dateFormat = (languageCode == "ko") ? "yyyy년 MM월 dd일" : "MMMM dd, yyyy"
43 | return dateFormatter.string(from: date)
44 | }
45 |
46 | static let shared = ArchivingViewModel()
47 | @Published var selectSegment: SelectEnum = .bookmark
48 | @Published var selectArtist: String = ""
49 | @Published var artistSet: Set = []
50 |
51 | func insertArtistSet(_ info: [ArchivedConcertInfo]) {
52 | if !artistSet.isEmpty {
53 | artistSet.removeAll()
54 | if !info.contains(where: { $0.artistInfo.name == selectArtist }) {
55 | selectArtist = ""
56 | }
57 | }
58 |
59 | // 임시 딕셔너리를 사용하여 아티스트 MBID별로 가장 최근 정보를 저장
60 | var uniqueArtists: [String: ArchivedConcertInfo] = [:]
61 |
62 | for concert in info {
63 | let artistMBID = concert.artistInfo.mbid
64 | // 이미 존재하는 MBID라면, 더 최신 정보로 업데이트 (또는 다른 기준 적용)
65 | if let existingConcert = uniqueArtists[artistMBID] {
66 | // 여기에 더 최신 정보를 선택하는 로직을 추가할 수 있습니다.
67 | // 예를 들어, 날짜를 비교하거나 다른 기준을 사용할 수 있습니다.
68 | uniqueArtists[artistMBID] = concert
69 | } else {
70 | uniqueArtists[artistMBID] = concert
71 | }
72 | }
73 |
74 | // 중복이 제거된 아티스트 정보를 Set에 삽입
75 | artistSet = Set(uniqueArtists.values)
76 |
77 | // for index in 0.. 1 {
27 | VStack {
28 | ScrollView(.horizontal) {
29 | HStack(spacing: 0) {
30 | ForEach(Array(bookmarkedSetlists.prefix(min(bookmarkedSetlists.count, 3))), id: \.self) { _ in
31 | setlistsLayer
32 | .padding(.horizontal, UIWidth * 0.05)
33 | }
34 | }
35 | .scrollTargetLayout()
36 | }
37 | .scrollTargetBehavior(.viewAligned)
38 | .scrollPosition(id: $scrollPosition)
39 | .scrollIndicators(.hidden)
40 |
41 | HStack {
42 | ForEach(Array(bookmarkedSetlists.prefix(min(bookmarkedSetlists.count, 3))), id: \.self) { element in
43 | if element == scrollPosition {
44 | RoundedRectangle(cornerRadius: 25.0)
45 | .frame(width: 15, height: 8)
46 | .foregroundStyle(Color.mainBlack)
47 | .padding(.vertical)
48 | .padding(.horizontal, 5)
49 | } else {
50 | Circle()
51 | .frame(width: 8)
52 | .foregroundStyle(Color.gray)
53 | .padding(.vertical)
54 | .padding(.horizontal, 5)
55 | }
56 | }
57 | }
58 | }
59 | } else {
60 | setlistsLayer
61 | }
62 | }
63 | .padding(.bottom)
64 | .onAppear { getBookmarkedSetlists() }
65 | }
66 |
67 | private func getBookmarkedSetlists() {
68 | bookmarkedSetlists = []
69 | for concert in concertInfo {
70 | if concert.artistInfo.mbid == vm.artistInfo.mbid {
71 | bookmarkedSetlists.append(concert)
72 | }
73 | }
74 | if !bookmarkedSetlists.isEmpty {
75 | scrollPosition = bookmarkedSetlists[0]
76 | }
77 | }
78 |
79 | private var emptyLayer: some View {
80 | SummarizedSetlistInfoView(
81 | type: .bookmarkedConcert,
82 | info: nil,
83 | infoButtonAction: nil,
84 | chevronButtonAction: nil
85 | )
86 | }
87 |
88 | private var setlistsLayer: some View {
89 | SummarizedSetlistInfoView(
90 | type: .bookmarkedConcert,
91 | info: SetlistInfo(
92 | artistInfo: vm.artistInfo,
93 | id: scrollPosition!.setlist.setlistId,
94 | date: vm.getFormattedDate(
95 | date: scrollPosition!.setlist.date,
96 | format: "yyyy년 MM월 dd일"
97 | ),
98 | title: scrollPosition!.setlist.title,
99 | venue: "\(scrollPosition!.setlist.venue)\n\(scrollPosition!.setlist.city), \(scrollPosition!.setlist.country)"
100 | ),
101 | infoButtonAction: nil,
102 | chevronButtonAction: {
103 | vm.archivingViewModel.selectSegment = .bookmark
104 | vm.archivingViewModel.selectArtist = vm.artistInfo.name
105 | if selectedTab == .archiving {
106 | dismiss()
107 | } else {
108 | selectedTab = .archiving
109 | }
110 | }
111 | )
112 | }
113 | }
114 |
115 | #Preview {
116 | ZStack {
117 | Color.gray.ignoresSafeArea()
118 | BookmarkedSetlistsView(vm: ArtistViewModel(), selectedTab: .constant(.archiving))
119 | }
120 | }
121 |
--------------------------------------------------------------------------------
/Projects/Feature/Scenes/MainScene/Component/ArtistImage.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ArtistImage.swift
3 | // Feature
4 | //
5 | // Created by 장수민 on 11/4/23.
6 | // Copyright © 2023 com.creative8.seta. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import SwiftUI
11 | import SwiftData
12 | import Core
13 | import UI
14 | import Combine
15 |
16 | struct ArtistImage: View {
17 | @Binding var selectedTab: Tab
18 | @Environment(\.colorScheme) var colorScheme
19 | var imageUrl: String
20 |
21 | var body: some View {
22 | VStack {
23 | Group {
24 | if !imageUrl.isEmpty {
25 | AsyncImage(url: URL(string: imageUrl)) { image in
26 | image
27 | .resizable()
28 | .aspectRatio(contentMode: .fill)
29 | .overlay(RoundedRectangle(cornerRadius: 15).stroke(Color(UIColor.systemGray5), lineWidth: 1))
30 | } placeholder: {
31 | ProgressView()
32 | }
33 | } else {
34 | artistEmptyImage
35 | }
36 | }
37 | .frame(height: UIHeight * 0.38)
38 | .clipShape(RoundedRectangle(cornerRadius: 15))
39 | .clipped()
40 | .safeAreaPadding(.horizontal, UIWidth * 0.07)
41 |
42 | }
43 | }
44 |
45 | public var artistEmptyImage: some View {
46 | Image("artistViewTicket", bundle: setaBundle)
47 | .resizable()
48 | .aspectRatio(contentMode: .fit)
49 | }
50 | }
51 |
52 | #Preview {
53 | ArtistImage(selectedTab: .constant(.home), imageUrl: "https://images-prod.dazeddigital.com/1428/azure/dazed-prod/1320/4/1324149.jpeg")
54 | }
55 |
--------------------------------------------------------------------------------
/Projects/Feature/Scenes/MainScene/Component/ArtistNameView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // HorizontalArtistNameScrollView.swift
3 | // Feature
4 | //
5 | // Created by 장수민 on 11/17/23.
6 | // Copyright © 2023 com.creative8.seta. All rights reserved.
7 | //
8 |
9 | import SwiftUI
10 | import MarqueeText
11 |
12 | struct ArtistNameView: View {
13 | @Binding var isShowToolTip: Bool
14 | @Binding var selectedTab: Tab
15 | @ObservedObject var viewModel: MainViewModel
16 | var index: Int
17 | var name: String
18 | @State private var textWidth: CGFloat = 0
19 | @State private var parentWidth: CGFloat = 0
20 |
21 | var body: some View {
22 | HStack(spacing: 0) {
23 | if textWidth > parentWidth {
24 | MarqueeText(
25 | text: name,
26 | font: .systemFont(ofSize: 32, weight: .semibold),
27 | leftFade: 16,
28 | rightFade: 16,
29 | startDelay: isShowToolTip ? 3 : 0
30 | )
31 | .frame(width: parentWidth)
32 | } else {
33 | Text(name)
34 | .font(.title)
35 | .fontWeight(.semibold)
36 | .lineLimit(1)
37 | .frame(width: UIWidth * 0.8, alignment: .center)
38 | }
39 | }
40 | .frame(width: UIWidth * 0.8, height: UIHeight * 0.06)
41 | .foregroundColor(viewModel.selectedIndex == index ? Color.mainBlack : Color.gray)
42 | .background(
43 | GeometryReader { geo in
44 | Color.clear
45 | .onAppear {
46 | parentWidth = geo.size.width
47 | measureTextWidth()
48 | }
49 | }
50 | )
51 | }
52 |
53 | private func measureTextWidth() {
54 | let text = Text(name)
55 | .font(.title)
56 | .fontWeight(.semibold)
57 |
58 | let controller = UIHostingController(rootView: text)
59 | controller.view.frame.size = .zero
60 | controller.view.sizeToFit()
61 | textWidth = controller.view.intrinsicContentSize.width
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/Projects/Feature/Scenes/MainScene/Component/ArtistsContentView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ArtistImageAndConcertListView.swift
3 | // Feature
4 | //
5 | // Created by 장수민 on 11/17/23.
6 | // Copyright © 2023 com.creative8.seta. All rights reserved.
7 | //
8 | import SwiftUI
9 | import Core
10 | import SwiftData
11 |
12 | struct ArtistsContentView: View {
13 | @Binding var selectedTab: Tab
14 | @StateObject var viewModel: MainViewModel
15 | @State var artistInfo: SaveArtistInfo
16 | @StateObject var dataManager = SwiftDataManager()
17 | @Environment(\.modelContext) var modelContext
18 | @Query(sort: \LikeArtist.orderIndex, order: .reverse) var likeArtists: [LikeArtist]
19 | @StateObject var tabViewManager: TabViewManager
20 |
21 | var index: Int
22 |
23 | var body: some View {
24 | let setlists: [Setlist?] = viewModel.setlists[index] ?? []
25 |
26 | NavigationStack(path: $tabViewManager.pageStack) {
27 | VStack(spacing: 24) {
28 |
29 | if viewModel.isLoading {
30 | loadingView
31 | } else {
32 | if let firstSetlist = setlists.compactMap({ $0 }).first {
33 | VStack(spacing: 12) {
34 | summarizedSetlistView(for: firstSetlist)
35 | ArtistMainSetlistView(viewModel: viewModel, index: index)
36 | }
37 |
38 | } else {
39 | emptySetlistView
40 | }
41 | }
42 | }
43 | .onAppear {
44 | dataManager.modelContext = modelContext
45 | }
46 | }
47 | }
48 |
49 | private var loadingView: some View {
50 | ProgressView()
51 | }
52 |
53 | private var emptySetlistView: some View {
54 | SummarizedSetlistInfoView(
55 | type: .recentConcert,
56 | info: nil,
57 | infoButtonAction: nil,
58 | chevronButtonAction: nil
59 | )
60 | }
61 |
62 | private func summarizedSetlistView(for setlist: Setlist) -> some View {
63 | let venueName = setlist.venue?.name ?? ""
64 | let city = setlist.venue?.city?.name ?? ""
65 | let countryName = setlist.venue?.city?.country?.name ?? ""
66 | let artistName = setlist.artist?.name ?? ""
67 |
68 | return SummarizedSetlistInfoView(
69 | type: .recentConcert,
70 | info: SetlistInfo(
71 | artistInfo: artistInfo.toArtistInfo(),
72 | id: setlist.id ?? "",
73 | date: viewModel.getDateFormatted(dateString: setlist.eventDate ?? ""),
74 | title: setlist.tour?.name ?? "\(artistName) Setlist",
75 | venue: "\(venueName)\n\(city), \(countryName)"
76 | ),
77 | infoButtonAction: nil,
78 | chevronButtonAction: {
79 | viewModel.navigateToArtistView = true
80 | }
81 | )
82 | }
83 | }
84 |
--------------------------------------------------------------------------------
/Projects/Feature/Scenes/MainScene/Component/EmptyMainView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // EmptyMain.swift
3 | // Feature
4 | //
5 | // Created by 장수민 on 11/4/23.
6 | // Copyright © 2023 com.creative8.seta. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import SwiftUI
11 | import SwiftData
12 | import Core
13 | import UI
14 |
15 | struct EmptyMainView: View {
16 | @Binding var selectedTab: Tab
17 |
18 | var body: some View {
19 | ZStack {
20 | Color.gray6.ignoresSafeArea()
21 | VStack {
22 | Spacer()
23 | Text("찜한 아티스트가 없어요")
24 | .font(.system(size: 16, weight: .semibold))
25 | .padding(.bottom, 9)
26 | .foregroundStyle(Color.mainBlack)
27 | Group {
28 | VStack {
29 | Text("검색에서 원하는 아티스트를 찾고")
30 | Text("하트버튼을 눌러주세요")
31 | }
32 | .foregroundStyle(Color.gray)
33 | }
34 | .font(.system(size: 13, weight: .regular))
35 | .multilineTextAlignment(.center)
36 | .padding(.bottom)
37 | Spacer()
38 | }
39 | }
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/Projects/Feature/Scenes/MainScene/Component/MainToolTipView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // MainToolTip.swift
3 | // Feature
4 | //
5 | // Created by 최효원 on 7/31/24.
6 | // Copyright © 2024 com.creative8.seta. All rights reserved.
7 | //
8 |
9 | import SwiftUI
10 |
11 | struct MainTooltipView: View {
12 | var body: some View {
13 | ZStack(alignment: .topTrailing) {
14 | CustomTriangleShape()
15 | .fill(Color.mainOrange)
16 | .offset(x: -50, y: 3)
17 | .frame(width: 20, height: 50)
18 |
19 | CustomRectangleShape(text: "메인 화면 아티스트를 수정할 수 있어요")
20 | .frame(width: 250, height: 40)
21 |
22 | }
23 | }
24 | }
25 |
26 | private struct CustomTriangleShape: Shape {
27 | private var width: CGFloat
28 | private var height: CGFloat
29 | private var radius: CGFloat
30 |
31 | fileprivate init(
32 | width: CGFloat = 20,
33 | height: CGFloat = 20,
34 | radius: CGFloat = 1
35 | ) {
36 | self.width = width
37 | self.height = height
38 | self.radius = radius
39 | }
40 |
41 | fileprivate func path(in rect: CGRect) -> Path {
42 | var path = Path()
43 |
44 | let topCenter = CGPoint(x: rect.minX + width / 2, y: rect.minY)
45 | let bottomLeft = CGPoint(x: rect.minX, y: rect.minY + height)
46 | let bottomRight = CGPoint(x: rect.minX + width, y: rect.minY + height)
47 |
48 | path.move(to: CGPoint(x: topCenter.x - radius, y: rect.minY))
49 | path.addArc(
50 | center: CGPoint(x: topCenter.x, y: rect.minY + radius),
51 | radius: radius,
52 | startAngle: Angle(degrees: 180),
53 | endAngle: Angle(degrees: 0),
54 | clockwise: false
55 | )
56 |
57 | path.addLine(to: bottomRight)
58 | path.addLine(to: bottomLeft)
59 | path.closeSubpath()
60 |
61 | return path
62 | }
63 | }
64 |
65 | private struct CustomRectangleShape: View {
66 | private var text: LocalizedStringKey
67 |
68 | fileprivate init(text: LocalizedStringKey) {
69 | self.text = text
70 | }
71 |
72 | fileprivate var body: some View {
73 | VStack {
74 | Spacer()
75 | Text(text)
76 | .font(.footnote)
77 | .foregroundColor(.white)
78 | .padding(.vertical, 10)
79 | .padding(.horizontal, 18)
80 | .background(
81 | RoundedRectangle(cornerRadius: 12)
82 | .fill(Color.mainOrange)
83 | )
84 | }
85 | }
86 | }
87 |
88 | #Preview {
89 | MainTooltipView()
90 | }
91 |
--------------------------------------------------------------------------------
/Projects/Feature/Scenes/NetworkUnavailableView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // NetworkUnavailableView.swift
3 | // Feature
4 | //
5 | // Created by 장수민 on 2/26/24.
6 | // Copyright © 2024 com.creative8.seta. All rights reserved.
7 | //
8 |
9 | import SwiftUI
10 | import Core
11 | import Foundation
12 |
13 | struct NetworkUnavailableView: View {
14 | @Environment(NetworkMonitor.self) private var networkMonitor
15 |
16 | var body: some View {
17 | ContentUnavailableView {
18 | Label("네트워크에 연결되어 있지 않습니다", systemImage: "wifi.exclamationmark")
19 | .foregroundStyle(Color.mainBlack)
20 | } description: {
21 | Text("인터넷 연결을 확인한 뒤 다시 시도해 주세요")
22 | .foregroundStyle(Color.mainBlack)
23 | } actions: {
24 | Button {
25 | networkMonitor.startMonitoring()
26 | } label: {
27 | Text("새로고침")
28 | .bold()
29 | .foregroundStyle(Color.mainWhite)
30 | .font(.system(size: 14))
31 | .padding(EdgeInsets(top: 17, leading: 22, bottom: 17, trailing: 22))
32 | .background(RoundedRectangle(cornerRadius: 14)
33 | .foregroundStyle(Color.mainBlack))
34 | }
35 | }
36 | .background(Color.mainWhite)
37 | }
38 | }
39 |
40 | #Preview {
41 | NetworkUnavailableView()
42 | }
43 |
--------------------------------------------------------------------------------
/Projects/Feature/Scenes/OnboardingScene/ViewModel/OnboardingViewModel.swift:
--------------------------------------------------------------------------------
1 | //
2 | // OnboardingViewModel.swift
3 | // Feature
4 | //
5 | // Created by 예슬 on 2023/10/22.
6 | // Copyright © 2023 com.creative8.seta. All rights reserved.
7 | //
8 |
9 | import SwiftUI
10 | import Foundation
11 | import Core
12 |
13 | public final class OnboardingViewModel: ObservableObject {
14 | @Published var selectedArtist: [OnboardingModel] = []
15 | @Published var selectedGenere: OnboardingFilterType = .all
16 | @Published var isShowToastBar = false
17 | @Published var artistFetchError = false
18 | @StateObject var dataManager = SwiftDataManager()
19 | @AppStorage("isOnboarding") var isOnboarding: Bool?
20 |
21 | init() {
22 | }
23 |
24 | func findFilterTagName(_ tag: OnboardingFilterType) -> String {
25 | switch(tag) {
26 | case .all: return ""
27 | case .kpop: return "K-Pop"
28 | case .pop: return "Pop"
29 | case .hiphop: return "Hip-Hop"
30 | case .rnb: return "R&B"
31 | case .rock: return "Rock"
32 | case .metal: return "Metal"
33 | case .elctronic: return "Electronic"
34 | case .folk: return "Country/Folk"
35 | case .jpop: return "J-Pop"
36 | }
37 | }
38 |
39 | func isKorean() -> Bool {
40 | guard let languageCode = Locale.current.language.languageCode?.identifier else { return false }
41 | return languageCode == "ko"
42 | }
43 | }
44 |
45 | // array nil 분기 처리
46 | extension Array {
47 | subscript (safe index: Int) -> Element? {
48 | return indices ~= index ? self[index] : nil
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/Projects/Feature/Scenes/SearchScene/Component/SearchArtistCell.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SearchArtistCell.swift
3 | // Feature
4 | //
5 | // Created by A_Mcflurry on 10/8/23.
6 | // Copyright © 2023 com.creative8. All rights reserved.
7 | //
8 |
9 | import SwiftUI
10 | import UI
11 | import Core
12 |
13 | struct SearchArtistCell: View {
14 | @Environment(\.colorScheme) var colorScheme
15 | @Binding var selectedTab: Tab
16 | let imageURL: String
17 | let artistName: String
18 | let artistAlias: String
19 | let artistMbid: String
20 | let artistGid: Int
21 |
22 | var body: some View {
23 | VStack(alignment: .leading) {
24 | NavigationLink(value: NavigationDelivery(artistInfo: SaveArtistInfo(name: artistName, country: "", alias: artistAlias, mbid: artistMbid, gid: artistGid, imageUrl: imageURL, songList: []))) {
25 | AsyncImage(url: URL(string: imageURL)) { phase in
26 | switch phase {
27 | case .empty:
28 | RoundedRectangle(cornerRadius: 12)
29 | .overlay(
30 | Group {
31 | if colorScheme == .light {
32 | Image(uiImage: UIImage(named: "artistViewTicket", in: Bundle(identifier: "com.creative8.seta.UI"), compatibleWith: nil)!)
33 | .centerCropped()
34 | .scaledToFill()
35 | .overlay(
36 | RoundedRectangle(cornerRadius: 12)
37 | .stroke(Color.gray, lineWidth: 1) // 색상과 선 두께를 원하는 대로 설정
38 | )
39 | } else {
40 | Image(uiImage: UIImage(named: "darkArtistViewTicket", in: Bundle(identifier: "com.creative8.seta.UI"), compatibleWith: nil)!)
41 | .centerCropped()
42 | .scaledToFill()
43 | .overlay(
44 | RoundedRectangle(cornerRadius: 12)
45 | .stroke(Color.gray, lineWidth: 1) // 색상과 선 두께를 원하는 대로 설정
46 | )
47 | }
48 | }
49 |
50 | )
51 | .foregroundStyle(Color(UIColor.systemGray5))
52 | case .success(let image):
53 | image
54 | .resizable()
55 | .scaledToFill()
56 | case .failure:
57 | RoundedRectangle(cornerRadius: 12)
58 | .overlay(
59 | Group {
60 | if colorScheme == .light {
61 | Image(uiImage: UIImage(named: "artistViewTicket", in: Bundle(identifier: "com.creative8.seta.UI"), compatibleWith: nil)!)
62 | .centerCropped()
63 | .scaledToFill()
64 | .overlay(
65 | RoundedRectangle(cornerRadius: 12)
66 | .stroke(Color.gray, lineWidth: 1) // 색상과 선 두께를 원하는 대로 설정
67 | )
68 | } else {
69 | Image(uiImage: UIImage(named: "darkArtistViewTicket", in: Bundle(identifier: "com.creative8.seta.UI"), compatibleWith: nil)!)
70 | .centerCropped()
71 | .scaledToFill()
72 | .overlay(
73 | RoundedRectangle(cornerRadius: 12)
74 | .stroke(Color.gray, lineWidth: 1) // 색상과 선 두께를 원하는 대로 설정
75 | )
76 | }
77 | }
78 |
79 | )
80 | .foregroundStyle(Color(UIColor.systemGray5))
81 | @unknown default:
82 | EmptyView()
83 | }
84 | }
85 | }
86 | .aspectRatio(contentMode: .fit)
87 | .clipShape(RoundedRectangle(cornerRadius: 12))
88 | .overlay(RoundedRectangle(cornerRadius: 12)
89 | .stroke(Color(UIColor.systemGray4), lineWidth: 1))
90 |
91 | Text("\(artistName)")
92 | .foregroundStyle(Color.mainBlack)
93 | .font(.footnote)
94 | .lineLimit(1)
95 | }
96 | }
97 | }
98 |
--------------------------------------------------------------------------------
/Projects/Feature/Scenes/SearchScene/Component/SearchArtistList.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SearchArtistList.swift
3 | // Feature
4 | //
5 | // Created by 고혜지 on 10/23/23.
6 | // Copyright © 2023 com.creative8.seta. All rights reserved.
7 | //
8 |
9 | import SwiftUI
10 | import SwiftData
11 | import Core
12 | import UI
13 |
14 | struct SearchArtistList: View {
15 | @Binding var selectedTab: Tab
16 | @ObservedObject var viewModel: SearchViewModel
17 | @StateObject var dataManager = SwiftDataManager()
18 | @Environment(\.modelContext) var modelContext
19 | @Query var searchHistory: [SearchHistory]
20 | var body: some View {
21 | if viewModel.isLoading {
22 | VStack {
23 | Spacer()
24 | ProgressView()
25 | .toolbar(.hidden, for: .tabBar)
26 | Spacer()
27 | }
28 | } else {
29 | ForEach(viewModel.artistList, id: \.name) { artist in
30 | let namePair: (String, String?) = viewModel.koreanConverter.findKoreanName(artist: artist)
31 | let info: String = ((namePair.1 != nil) ? namePair.1! + ", " : "") + (artist.area?.name ?? "")
32 | VStack(alignment: .leading) {
33 | NavigationLink {
34 | ArtistView(selectedTab: $selectedTab, artistName: namePair.0, artistAlias: namePair.1, artistMbid: artist.id ?? "")
35 | } label: {
36 | ListRow(namePair: namePair, info: info)
37 | }
38 | .simultaneousGesture(TapGesture().onEnded {
39 | dataManager.findHistoryAndDelete(searchHistory, artist.id ?? "")
40 | dataManager.addSearchHistory(name: namePair.0, country: info, alias: namePair.1 ?? "", mbid: artist.id ?? "", gid: 0, imageUrl: "", songList: [])
41 | })
42 |
43 | Divider()
44 | .foregroundStyle(Color.gray)
45 | }
46 | }
47 | .onAppear { dataManager.modelContext = modelContext }
48 | .toolbar(.hidden, for: .tabBar)
49 | }
50 |
51 | }
52 | }
53 |
54 | public struct ListRow: View {
55 | let namePair: (String, String?)
56 | let info: String
57 |
58 | public var body: some View {
59 | HStack {
60 | VStack(alignment: .leading) {
61 | Text(namePair.0)
62 | .font(.subheadline)
63 | .foregroundStyle(Color.mainBlack)
64 | .lineLimit(1)
65 |
66 | Group {
67 | if info == "" {
68 | Text(" ")
69 | } else {
70 | Text(info)
71 | }
72 | }
73 | .lineLimit(1)
74 | .font(.footnote)
75 | .foregroundStyle(Color(UIColor.systemGray2))
76 |
77 | }
78 | Spacer()
79 | }
80 | .padding(.vertical, 5)
81 | }
82 | }
83 |
--------------------------------------------------------------------------------
/Projects/Feature/Scenes/SearchScene/Component/SearchHistoryCell.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SearchHistoryCell.swift
3 | // Feature
4 | //
5 | // Created by A_Mcflurry on 10/9/23.
6 | // Copyright © 2023 com.creative8. All rights reserved.
7 | //
8 |
9 | import SwiftUI
10 | import SwiftData
11 | import Core
12 | import UI
13 |
14 | struct SearchHistoryCell: View {
15 | @Binding var searchText: String
16 | @Binding var selectedTab: Tab
17 | let history: SearchHistory
18 | let dataManager: SwiftDataManager
19 |
20 | var body: some View {
21 | VStack {
22 | HStack {
23 | NavigationLink {
24 | ArtistView(selectedTab: $selectedTab, artistName: history.artistInfo.name, artistAlias: history.artistInfo.alias, artistMbid: history.artistInfo.mbid)
25 | } label: {
26 | ListRow(namePair: (history.artistInfo.name, ""), info: history.artistInfo.country)
27 | }
28 |
29 | Spacer()
30 |
31 | Button {
32 | dataManager.deleteSearchHistory(history)
33 | } label: {
34 | Image(systemName: "xmark")
35 | .foregroundStyle(Color(UIColor.systemGray3))
36 | .padding(.trailing, 12)
37 | }
38 | }
39 | .padding(.top)
40 |
41 | Divider()
42 | .foregroundStyle(Color(UIColor.systemGray3))
43 | .padding(.top, 15)
44 | }
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/Projects/Feature/Scenes/SearchScene/Component/SearchbarCustom.swift:
--------------------------------------------------------------------------------
1 | //
2 | // UISearchbarCustom.swift
3 | // Feature
4 | //
5 | // Created by A_Mcflurry on 10/8/23.
6 | // Copyright © 2023 com.creative8. All rights reserved.
7 | //
8 |
9 | import SwiftUI
10 | import Core
11 | import UI
12 |
13 | struct SearchBar: View {
14 | @Binding var text: String
15 | @Binding var isEditing: Bool
16 | var placeholder: LocalizedStringResource = "아티스트를 검색하세요"
17 |
18 | init(text: Binding, isEditing: Binding) {
19 | self._text = text
20 | self._isEditing = isEditing
21 | }
22 |
23 | var body: some View {
24 | HStack {
25 | TextField("", text: $text, prompt: Text(placeholder))
26 | .autocorrectionDisabled(true)
27 | .padding(7)
28 | .padding(.horizontal, 34)
29 | .foregroundColor(Color.gray)
30 | .background(Color.mainWhite)
31 | .cornerRadius(10)
32 | .overlay(
33 | HStack {
34 | Image(systemName: "magnifyingglass")
35 | .foregroundColor(Color.gray)
36 | .frame(minWidth: 0, maxWidth: .infinity, alignment: .leading)
37 | .padding(.leading, 16)
38 |
39 | if isEditing && text.count != 0 {
40 | Button {
41 | self.text = ""
42 | } label: {
43 | Image(systemName: "multiply.circle.fill")
44 | .foregroundColor(Color.gray)
45 | .padding(.trailing, 7.5)
46 | }
47 | }
48 | }
49 | )
50 | .onTapGesture {
51 | withAnimation {
52 | self.isEditing = true
53 | }
54 | AnalyticsEvent.trackButtonTap(buttonName: AnalyticsEvent.Event.searchBar)
55 | }
56 |
57 | if isEditing {
58 | Button("취소") {
59 | withAnimation {
60 | self.isEditing = false
61 | self.text = ""
62 | }
63 | UIApplication.shared.sendAction(#selector(UIResponder.resignFirstResponder), to: nil, from: nil, for: nil)
64 | }
65 | .foregroundStyle(Color.mainBlack)
66 | .padding(.horizontal, 10)
67 | }
68 | }
69 | .textCase(.none)
70 | .padding(.bottom, 20)
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/Projects/Feature/Scenes/SearchScene/ViewModel/SearchViewModel.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SearchViewModel.swift
3 | // Feature
4 | //
5 | // Created by 최효원 on 10/7/23.
6 | // Copyright © 2023 com.creative8. All rights reserved.
7 | //
8 |
9 | import SwiftUI
10 | import Core
11 | import Combine
12 |
13 | final class SearchViewModel: ObservableObject {
14 | let dataService = SetlistDataService()
15 | let artistDataManager = ArtistDataManager()
16 | let koreanConverter = KoreanConverter()
17 | let artistFetchService = ArtistFetchService()
18 |
19 | @Published var searchText: String = ""
20 | @Published var searchIsPresented: Bool = false
21 | @Published var artistList: [MusicBrainzArtist] = []
22 | @Published var isLoading: Bool = false
23 | @Published var domesticArtists: [OnboardingModel] = []
24 | @Published var foreignArtists: [OnboardingModel] = []
25 | private var cancellables = Set()
26 |
27 | init() {
28 | let searchTextPublisher = $searchText
29 | .debounce(for: .seconds(0.5), scheduler: DispatchQueue.main)
30 | .removeDuplicates()
31 | searchTextPublisher
32 | .sink { [weak self] _ in
33 | self?.getSearchArtistList()
34 | }
35 | .store(in: &cancellables)
36 |
37 | if artistFetchService.allArtist.isEmpty {
38 | artistFetchService.fetchData { [weak self] success in
39 | guard let self else { return }
40 | if success {
41 | getRandomArtistInfos()
42 | }
43 | }
44 | }
45 | }
46 |
47 | func getRandomArtistInfos() {
48 | if searchIsPresented { return }
49 |
50 | var domestic: [OnboardingModel] = []
51 | var foreign: [OnboardingModel] = []
52 |
53 | for data in artistFetchService.allArtist {
54 | guard data.url != nil && data.url != "" && data.url != "https://assets.genius.com/images/default_avatar_300.png?1722887932" else { continue }
55 | guard let tags = data.tags else { continue }
56 |
57 | if tags.contains("K-Pop") && data.country == "South Korea" {
58 | domestic.append(data)
59 | } else if data.country != "South Korea" && !data.country.isEmpty {
60 | foreign.append(data)
61 | }
62 | }
63 | domesticArtists = Array(domestic.shuffled().prefix(9))
64 | foreignArtists = Array(foreign.shuffled().prefix(9))
65 | }
66 |
67 | func getSearchArtistList() {
68 | self.isLoading = true
69 | Future<[MusicBrainzArtist], Error> { promise in
70 | self.dataService.searchArtistsFromMusicBrainz(artistName: self.searchText) { result in
71 | if let result = result {
72 | promise(.success(result.artists ?? []))
73 | } else {
74 | promise(.failure(NSError(domain: "YourDomain", code: 1, userInfo: nil)))
75 | }
76 | }
77 | }
78 | .receive(on: DispatchQueue.main)
79 | .sink { completion in
80 | switch completion {
81 | case .finished:
82 | self.isLoading = false
83 | case .failure(let error):
84 | print("Failed to fetch musicbrainz data: \(error)")
85 | }
86 | } receiveValue: { artists in
87 | self.artistList = artists
88 | }
89 | .store(in: &cancellables)
90 | }
91 | }
92 |
--------------------------------------------------------------------------------
/Projects/Feature/Scenes/SetlistScene/Component/MusicButtonView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // MusicButtonView.swift
3 | // Feature
4 | //
5 | // Created by A_Mcflurry on 7/14/24.
6 | // Copyright © 2024 com.creative8.seta. All rights reserved.
7 | //
8 |
9 | import SwiftUI
10 | import UI
11 |
12 | struct MusicButtonView: View {
13 | let music: MusicCases
14 | var body: some View {
15 | VStack {
16 | Image(music.rawValue, bundle: setaBundle)
17 | .resizable()
18 | .frame(width: 40, height: 40)
19 | .frame(maxWidth: .infinity)
20 | .padding(.vertical, 21)
21 | .background(Color.gray6)
22 | .clipShape(RoundedRectangle(cornerRadius: 14))
23 |
24 | Text(music.name)
25 | .font(.system(size: 15))
26 | .foregroundStyle(Color.mainBlack)
27 | .opacity(0.6)
28 | }
29 |
30 | }
31 |
32 | enum MusicCases: String {
33 | case appleMusic
34 | case spotify
35 |
36 | var name: String {
37 | switch self {
38 | case .appleMusic:
39 | return "Apple Music"
40 | case .spotify:
41 | return "Spotify"
42 | }
43 | }
44 | }
45 | }
46 |
47 | #Preview {
48 | HStack {
49 | MusicButtonView(music: .appleMusic)
50 | MusicButtonView(music: .spotify)
51 | }
52 | .padding()
53 | }
54 |
--------------------------------------------------------------------------------
/Projects/Feature/Scenes/SetlistScene/Component/SetlistFMLinkButtonView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SetlistFMLinkButtonView.swift
3 | // Feature
4 | //
5 | // Created by 고혜지 on 11/7/23.
6 | // Copyright © 2023 com.creative8.seta. All rights reserved.
7 | //
8 |
9 | import SwiftUI
10 | import UI
11 |
12 | struct SetlistFMLinkButtonView: View {
13 | var body: some View {
14 | VStack {
15 | if let url = URL(string: "https://www.setlist.fm") {
16 | Link(destination: url) {
17 | Text("세트리스트 추가하기")
18 | .foregroundStyle(Color.mainBlack)
19 | .font(.callout)
20 | .fontWeight(.semibold)
21 | .frame(maxWidth: .infinity)
22 | .padding(.vertical, 20)
23 | .background(Color.gray)
24 | .cornerRadius(14)
25 | }
26 | }
27 | }
28 | }
29 | }
30 |
31 | #Preview {
32 | SetlistFMLinkButtonView()
33 | }
34 |
--------------------------------------------------------------------------------
/Projects/Feature/Scenes/SetlistScene/Component/ShareOptionButtonView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ShareOptionButtonView.swift
3 | // Feature
4 | //
5 | // Created by 예슬 on 6/10/24.
6 | // Copyright © 2024 com.creative8.seta. All rights reserved.
7 | //
8 |
9 | import SwiftUI
10 |
11 | struct ShareOptionButtonView: View {
12 | let action: () -> Void
13 | let label: LocalizedStringKey
14 | let systemImageName: String
15 |
16 | var body: some View {
17 | Button(action: {
18 | action()
19 | }, label: {
20 | HStack {
21 | Text(label)
22 | .font(.callout)
23 | .fontWeight(.semibold)
24 | Spacer()
25 | Image(systemName: systemImageName)
26 | }
27 | .foregroundStyle(.black)
28 | })
29 | .padding(EdgeInsets(top: 19, leading: 20, bottom: 19, trailing: 16))
30 | }
31 | }
32 |
33 | struct CustomDivider: View {
34 | var body: some View {
35 | Rectangle()
36 | .frame(height: 0.33)
37 | .foregroundColor(.gray)
38 | .padding(.leading, 20)
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/Projects/Feature/Scenes/SetlistScene/Component/ToastMessageView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ToastMessageView.swift
3 | // Feature
4 | //
5 | // Created by 고혜지 on 11/7/23.
6 | // Copyright © 2023 com.creative8.seta. All rights reserved.
7 | //
8 |
9 | import SwiftUI
10 | import UI
11 |
12 | struct ToastMessageView: View {
13 | let message: LocalizedStringResource
14 | let subMessage: LocalizedStringResource?
15 | let icon: String
16 | let color: Color
17 |
18 | var body: some View {
19 | HStack {
20 | VStack(alignment: .leading) {
21 | Text(message)
22 | .foregroundStyle(Color.mainWhite)
23 | .font(.subheadline)
24 | if let subMessage = subMessage {
25 | Text(subMessage)
26 | .foregroundStyle(Color.toast1)
27 | .font(.subheadline)
28 | }
29 | }
30 | Spacer()
31 | Image(systemName: icon)
32 | .renderingMode(.template)
33 | .foregroundStyle(color)
34 | }
35 | .padding(15)
36 | .frame(maxWidth: .infinity)
37 | .background(Blur().cornerRadius(6.0))
38 | .background(Color.toastBG.cornerRadius(6.0))
39 | }
40 | }
41 |
42 | open class UIBackdropView: UIView {
43 |
44 | open override class var layerClass: AnyClass {
45 | NSClassFromString("CABackdropLayer") ?? CALayer.self
46 | }
47 | }
48 |
49 | public struct Backdrop: UIViewRepresentable {
50 |
51 | public init() {}
52 |
53 | public func makeUIView(context: Context) -> UIBackdropView {
54 | UIBackdropView()
55 | }
56 |
57 | public func updateUIView(_ uiView: UIBackdropView, context: Context) {}
58 | }
59 |
60 | public struct Blur: View {
61 |
62 | public var radius: CGFloat
63 | public var opaque: Bool
64 |
65 | public init(radius: CGFloat = 3.0, opaque: Bool = false) {
66 | self.radius = radius
67 | self.opaque = opaque
68 | }
69 |
70 | public var body: some View {
71 | Backdrop()
72 | .blur(radius: radius, opaque: opaque)
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/Projects/Feature/Scenes/SetlistScene/View/EmptySetlistView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // EmptySetlistView.swift
3 | // Feature
4 | //
5 | // Created by 고혜지 on 11/7/23.
6 | // Copyright © 2023 com.creative8.seta. All rights reserved.
7 | //
8 |
9 | import SwiftUI
10 | import UI
11 |
12 | struct EmptySetlistView: View {
13 | @StateObject var vm = MainViewModel()
14 | var body: some View {
15 | VStack {
16 | Text("등록된 세트리스트가 없어요")
17 | .font(.headline)
18 | .fontWeight(.semibold)
19 | .padding(.top, UIHeight * 0.1)
20 | .padding(.bottom, 5)
21 | Text("세트리스트를 직접 작성하고 싶으신가요?")
22 | .multilineTextAlignment(.center)
23 | .font(.footnote)
24 | .foregroundStyle(Color.gray)
25 | HStack(spacing: 0) {
26 | if vm.isKorean() {
27 | Link(destination: URL(string: "https://www.setlist.fm")!) {
28 | Text("Setlist.fm")
29 | .underline()
30 | }
31 | Text("에서 추가하세요.")
32 | } else {
33 | Text("setlist? Add it on ")
34 | Link(destination: URL(string: "https://www.setlist.fm")!) {
35 | Text("Setlist.fm.")
36 | .underline()
37 | }
38 | }
39 | }
40 | .foregroundStyle(Color.gray)
41 | .font(.footnote)
42 | }
43 | }
44 | }
45 |
46 | #Preview {
47 | EmptySetlistView()
48 | }
49 |
--------------------------------------------------------------------------------
/Projects/Feature/Scenes/SetlistScene/View/ListView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ListView.swift
3 | // Feature
4 | //
5 | // Created by 고혜지 on 11/7/23.
6 | // Copyright © 2023 com.creative8.seta. All rights reserved.
7 | //
8 |
9 | import SwiftUI
10 | import Core
11 | import UI
12 |
13 | struct ListView: View {
14 | let setlist: Setlist?
15 | let artistInfo: ArtistInfo?
16 | @ObservedObject var vm: SetlistViewModel
17 |
18 | var body: some View {
19 | VStack {
20 | ForEach(setlist?.sets?.setsSet ?? [], id: \.self) { session in
21 | VStack(alignment: .leading) {
22 | Text(session.name != nil ? session.name! : session.encore != nil ? "Encore" : "Main")
23 | .font(.headline)
24 | .fontWeight(.bold)
25 | .foregroundStyle(Color(UIColor.systemGray2))
26 | .padding(.top, 30)
27 | let songs = session.song ?? []
28 | ForEach(Array(songs.enumerated()), id: \.offset) { index, song in
29 | if let title = song.name {
30 |
31 | Group {
32 | if song.tape != nil && song.tape == true {
33 | ListRowView(
34 | index: nil,
35 | title: vm.koreanConverter.findKoreanTitle(title: title, songList: artistInfo?.songList ?? []) ?? title,
36 | info: song.info
37 | )
38 | .opacity(0.6)
39 | } else {
40 | ListRowView(
41 | index: index + 1,
42 | title: vm.koreanConverter.findKoreanTitle(title: title, songList: artistInfo?.songList ?? []) ?? title,
43 | info: song.info
44 | )
45 | }
46 | }
47 | .padding(.vertical, 7)
48 |
49 | if index + 1 < songs.count {
50 | Divider().foregroundStyle(Color(UIColor.systemGray3))
51 | }
52 | }
53 | }
54 | }
55 | }
56 | }
57 | }
58 | }
59 |
60 | private struct ListRowView: View {
61 | var index: Int?
62 | var title: String
63 | var info: String?
64 |
65 | var body: some View {
66 | HStack(alignment: .top, spacing: 20) {
67 | if let index = index {
68 | Text(String(format: "%02d", index))
69 | .foregroundStyle(Color.mainBlack)
70 | } else {
71 | Image(systemName: "recordingtape")
72 | .foregroundStyle(Color.mainBlack)
73 | }
74 |
75 | VStack(alignment: .leading, spacing: 5) {
76 | Text(title)
77 | .foregroundStyle(Color.mainBlack)
78 | .lineLimit(1)
79 |
80 | if let info = info {
81 | Text(info)
82 | .fontWeight(.regular)
83 | .foregroundStyle(Color(UIColor.systemGray2))
84 | }
85 | }
86 | }
87 | .font(.callout)
88 | .fontWeight(.semibold)
89 | .frame(maxWidth: .infinity, alignment: .leading)
90 | }
91 | }
92 |
--------------------------------------------------------------------------------
/Projects/Feature/Scenes/SetlistScene/ViewModel/ExportPlaylistViewModel.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ExportPlaylistViewModel.swift
3 | // Feature
4 | //
5 | // Created by 장수민 on 11/21/23.
6 | // Copyright © 2023 com.creative8.seta. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import Photos
11 | import Core
12 | import MusicKit
13 |
14 | final class ExportPlaylistViewModel: ObservableObject {
15 | @Published var playlistTitle: String = ""
16 | @Published var showAppleMusicAlert: Bool = false
17 | @Published var showToastMessageAppleMusic: Bool = false
18 | @Published var showToastMessageSubscription: Bool = false
19 | @Published var showToastMessageCapture = false
20 | @Published var showLibrarySettingsAlert = false
21 | @Published var showMusicSettingsAlert = false
22 | @Published var showSpotifyAlert = false
23 | @Published var showCaptureAlert = false
24 |
25 | // MARK: Photo
26 | func checkPhotoPermission() -> Bool {
27 | var status: PHAuthorizationStatus = .notDetermined
28 |
29 | if #available(iOS 14, *) {
30 | status = PHPhotoLibrary.authorizationStatus()
31 | } else {
32 | status = PHPhotoLibrary.authorizationStatus()
33 | }
34 | return status == .denied || status == .notDetermined
35 |
36 | }
37 |
38 | func requestPhotoLibraryPermission() {
39 | PHPhotoLibrary.requestAuthorization { status in
40 | switch status {
41 | case .authorized:
42 | // 권한이 허용된 경우
43 | print("Photo library access granted")
44 | case .denied, .restricted:
45 | // 권한이 거부되거나 제한된 경우
46 | print("Photo library access denied")
47 | case .notDetermined:
48 | // 권한이 아직 결정되지 않은 경우
49 | print("Photo library access not determined")
50 | case .limited:
51 | print("Photo library access limited")
52 | @unknown default:
53 | print("omg")
54 | }
55 | }
56 | }
57 |
58 | func getPhotoLibraryPermissionStatus() -> PHAuthorizationStatus {
59 | let status = PHPhotoLibrary.authorizationStatus()
60 | return status
61 | }
62 |
63 | func handlePhotoExportButtonAction() {
64 | if self.getPhotoLibraryPermissionStatus() == .notDetermined {
65 | self.requestPhotoLibraryPermission()
66 | } else if self.getPhotoLibraryPermissionStatus() == .denied {
67 | self.showLibrarySettingsAlert = true
68 | } else {
69 | self.showToastMessageCapture = true
70 | DispatchQueue.main.asyncAfter(deadline: .now() + 3.0) {
71 | self.showToastMessageCapture = false
72 | }
73 | }
74 | }
75 |
76 | // MARK: AppleMusic
77 |
78 | var musicKitPermissionStatus: MusicAuthorization.Status {
79 | return MusicAuthorization.currentStatus
80 | }
81 |
82 | func addToAppleMusic(musicList: [(String, String?)], setlist: Setlist?) {
83 | AppleMusicService().addPlayList(
84 | name: self.playlistTitle,
85 | musicList: musicList,
86 | venue: setlist?.venue?.name
87 | )
88 | self.showAppleMusicAlert = false
89 | self.showToastMessageAppleMusic = true
90 | DispatchQueue.main.asyncAfter(deadline: .now() + 3.0) {
91 | self.showToastMessageAppleMusic = false
92 | }
93 | }
94 |
95 | func handleAppleMusicButtonAction() {
96 | Task {
97 | switch musicKitPermissionStatus {
98 | case .notDetermined:
99 | AppleMusicService().requestMusicAuthorization()
100 | case .denied, .restricted:
101 | self.showMusicSettingsAlert = true
102 | case .authorized:
103 | let subscriptionChecker = CheckAppleMusicSubscription.shared
104 | subscriptionChecker.appleMusicSubscription { [self] isSubscribed in
105 |
106 | if isSubscribed {
107 | self.showAppleMusicAlert.toggle()
108 | playlistTitle = ""
109 | } else {
110 | self.showToastMessageSubscription = true
111 | DispatchQueue.main.asyncAfter(deadline: .now() + 3.0) {
112 | self.showToastMessageSubscription = false
113 | }
114 | }
115 | }
116 | default: break
117 | }
118 | }
119 | }
120 | }
121 |
--------------------------------------------------------------------------------
/Projects/Feature/Scenes/SetlistScene/ViewModel/SetlistViewModel.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SetlistViewModel.swift
3 | // Feature
4 | //
5 | // Created by 고혜지 on 10/14/23.
6 | // Copyright © 2023 com.creative8. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import Core
11 |
12 | final class SetlistViewModel: ObservableObject {
13 | @Published var isBookmarked: Bool
14 | @Published var showModal: Bool
15 | @Published var isLoading: Bool
16 |
17 | let koreanConverter = KoreanConverter()
18 | let dataService = SetlistDataService()
19 | let artistDataManager = ArtistDataManager()
20 | let dataManager = SwiftDataManager()
21 |
22 | // [(songTitle, artistName)]
23 | var setlistSongName: [(String, String?)] = []
24 | var setlistSongKoreanName: [(String, String?)] = []
25 |
26 | init() {
27 | self.isBookmarked = false
28 | self.showModal = false
29 | self.isLoading = false
30 | }
31 |
32 | func isKorean() -> Bool {
33 | guard let languageCode = Locale.current.language.languageCode?.identifier else { return false }
34 | return languageCode == "ko"
35 | }
36 |
37 | func isEmptySetlist(_ setlist: Setlist) -> Bool {
38 | return setlist.sets?.setsSet?.isEmpty == true
39 | }
40 |
41 | func getFormattedDateFromString(date: String, format: String) -> String? {
42 | let inputDateFormatter: DateFormatter = {
43 | let formatter = DateFormatter()
44 | formatter.dateFormat = "dd-MM-yyyy"
45 | return formatter
46 | }()
47 |
48 | let outputDateFormatter: DateFormatter = {
49 | let formatter = DateFormatter()
50 | formatter.dateFormat = format
51 | formatter.locale = Locale(identifier: "en_US")
52 | return formatter
53 | }()
54 |
55 | if let inputDate = inputDateFormatter.date(from: date) {
56 | return outputDateFormatter.string(from: inputDate)
57 | } else {
58 | return nil
59 | }
60 | }
61 |
62 | func getDateFormatted(date: Date?) -> String {
63 | guard let date = date else { return "-" }
64 | guard let languageCode = Locale.current.language.languageCode?.identifier else { return "" }
65 |
66 | let dateFormatter = DateFormatter()
67 |
68 | dateFormatter.dateFormat = (languageCode == "ko") ? "yyyy년 MM월 dd일" : "MMMM dd, yyyy"
69 | return dateFormatter.string(from: date)
70 | }
71 |
72 | func allDateFormatter(inputDate: String) -> String? {
73 | guard let languageCode = Locale.current.language.languageCode?.identifier else { return "" }
74 |
75 | let dateFormatter = DateFormatter()
76 | dateFormatter.dateFormat = "dd-MM-yyyy"
77 |
78 | // 입력된 날짜 문자열을 "dd-MM-yyyy" 형식으로 변환
79 | guard let convertedDate = dateFormatter.date(from: inputDate) else {
80 | return ""
81 | }
82 | // 날짜 형식 어떻게 보여줄지? 정하기
83 | dateFormatter.dateFormat = (languageCode == "ko") ? "yyyy년 MM월 dd일" : "MMMM dd, yyyy"
84 |
85 | // 변환된 날짜를 설정한 형식으로 문자열로 반환
86 | return dateFormatter.string(from: convertedDate)
87 | }
88 |
89 | func convertDateStringToDate(_ dateString: String, format: String = "dd-MM-yyyy") -> Date? {
90 | let dateFormatter = DateFormatter()
91 | dateFormatter.dateFormat = format
92 | dateFormatter.locale = Locale(identifier: "en_US_POSIX") // Optional: Specify the locale
93 |
94 | if let date = dateFormatter.date(from: dateString) {
95 | return date
96 | } else {
97 | return nil // 날짜 형식이 맞지 않을 경우 nil 반환
98 | }
99 | }
100 |
101 | func createArrayForExportPlaylist(setlist: Setlist?, songList: [Titles], artistName: String?) {
102 |
103 | setlistSongName = []
104 | setlistSongKoreanName = []
105 |
106 | for session in setlist?.sets?.setsSet ?? [] {
107 | for song in session.song ?? [] {
108 | if let title = song.name {
109 | var name: String?
110 | if let cover = song.cover?.name { // 커버곡이면
111 | name = cover
112 | } else { // 커버곡이 아니면
113 | name = artistName
114 | }
115 |
116 | // 영문 배열에 추가
117 | self.setlistSongName.append((title, name))
118 |
119 | // 한글 배열에 추가
120 | let tmp = self.koreanConverter.findKoreanTitle(title: title, songList: songList) ?? title
121 | self.setlistSongKoreanName.append((tmp, name))
122 |
123 | }
124 |
125 | }
126 | }
127 |
128 | }
129 | }
130 |
--------------------------------------------------------------------------------
/Projects/Feature/Scenes/SettingScene/View/AskView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AskView.swift
3 | // Feature
4 | //
5 | // Created by 예슬 on 2023/10/15.
6 | // Copyright © 2023 com.creative8. All rights reserved.
7 | //
8 |
9 | import SwiftUI
10 | import MessageUI
11 | import UI
12 |
13 | struct AskView: View {
14 | @State var isMailErrorAlertPresented: Bool = false
15 | @StateObject var coordinator = Coordinator()
16 |
17 | var body: some View {
18 | Button(action: {
19 | commentsButtonTapped()
20 | }, label: {
21 | LinkLabelView(linkLabel: "문의하기")
22 | })
23 | .alert(isPresented: $isMailErrorAlertPresented) {
24 | Alert(
25 | title: Text("메일 전송 실패"),
26 | message: Text("메일을 보내려면 'Mail' 앱이 필요합니다. App Store에서 해당 앱을 복원하거나 이메일 설정을 확인하고 다시 시도해주세요."),
27 | primaryButton: .default(Text("App Store로 이동하기")) {
28 | if let url = URL(string: "https://apps.apple.com/kr/app/mail/id1108187098"), UIApplication.shared.canOpenURL(url) {
29 | if #available(iOS 10.0, *) {
30 | UIApplication.shared.open(url, options: [:], completionHandler: nil)
31 | } else {
32 | UIApplication.shared.openURL(url)
33 | }
34 | }
35 | },
36 | secondaryButton: .destructive(Text("취소"))
37 | )
38 | }
39 | }
40 |
41 | func commentsButtonTapped() {
42 | guard let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene,
43 | let rootViewController = windowScene.windows.first?.rootViewController else {
44 | return
45 | }
46 |
47 | if MFMailComposeViewController.canSendMail() {
48 | let composeViewController = MFMailComposeViewController()
49 | composeViewController.mailComposeDelegate = coordinator
50 |
51 | let bodyString =
52 | """
53 | 안녕하세요. Seta 입니다. 아래 내용을 보내주시면 문의 확인에 도움이 됩니다.
54 | Hello, this is Seta. Providing the following information will help us investigate your inquiry
55 |
56 | - iOS 버전(iOS version) :
57 | - 기기 모델명(Device model) :
58 | - 문제발생일시(Date and time of issue) :
59 | - 문의 내용(Details of your inquiry) :
60 |
61 |
62 | """
63 |
64 | composeViewController.setToRecipients(["thecreative8team@gmail.com"])
65 | composeViewController.setSubject(" 문의 및 의견")
66 | composeViewController.setMessageBody(bodyString, isHTML: false)
67 | composeViewController.view.tintColor = .systemBlue
68 |
69 | if let presented = rootViewController.presentedViewController {
70 | presented.dismiss(animated: false) {
71 | rootViewController.present(composeViewController, animated: true, completion: nil)
72 | }
73 | } else {
74 | rootViewController.present(composeViewController, animated: true, completion: nil)
75 | }
76 | } else {
77 | isMailErrorAlertPresented.toggle()
78 | }
79 | }
80 |
81 | class Coordinator: NSObject, MFMailComposeViewControllerDelegate, ObservableObject {
82 | func mailComposeController(
83 | _ controller: MFMailComposeViewController, didFinishWith
84 | result: MFMailComposeResult,
85 | error: Error?) {
86 | controller.dismiss(animated: true, completion: nil)
87 | }
88 | }
89 | }
90 |
91 | #Preview {
92 | AskView()
93 | }
94 |
--------------------------------------------------------------------------------
/Projects/UI/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DeveloperAcademy-POSTECH/2023-MacC-Team12-Creative8/4b68cf6d2cee5683693269560ffbd23162034ec0/Projects/UI/.DS_Store
--------------------------------------------------------------------------------
/Projects/UI/Project.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Projects.swift
3 | // ProjectDescriptionHelpers
4 | //
5 | // Created by 최효원 on 2023/10/06.
6 | //
7 |
8 | import ProjectDescription
9 | import ProjectDescriptionHelpers
10 |
11 | let project = Project.makeModule(
12 | name: "UI",
13 | product: .framework,
14 | dependencies: [
15 |
16 | ],
17 | sources: ["Sources/**"],
18 | resources: ["Resources/**"]
19 | )
20 |
--------------------------------------------------------------------------------
/Projects/UI/Resources/Colors.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/Projects/UI/Resources/Colors.xcassets/ellipsis.colorset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "colors" : [
3 | {
4 | "color" : {
5 | "color-space" : "srgb",
6 | "components" : {
7 | "alpha" : "1.000",
8 | "blue" : "0xFF",
9 | "green" : "0xFF",
10 | "red" : "0xFF"
11 | }
12 | },
13 | "idiom" : "universal"
14 | },
15 | {
16 | "appearances" : [
17 | {
18 | "appearance" : "luminosity",
19 | "value" : "dark"
20 | }
21 | ],
22 | "color" : {
23 | "color-space" : "srgb",
24 | "components" : {
25 | "alpha" : "1.000",
26 | "blue" : "0xEE",
27 | "green" : "0xEA",
28 | "red" : "0xE9"
29 | }
30 | },
31 | "idiom" : "universal"
32 | }
33 | ],
34 | "info" : {
35 | "author" : "xcode",
36 | "version" : 1
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/Projects/UI/Resources/Colors.xcassets/gray6.colorset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "colors" : [
3 | {
4 | "color" : {
5 | "color-space" : "srgb",
6 | "components" : {
7 | "alpha" : "1.000",
8 | "blue" : "0xF6",
9 | "green" : "0xF2",
10 | "red" : "0xF2"
11 | }
12 | },
13 | "idiom" : "universal"
14 | },
15 | {
16 | "appearances" : [
17 | {
18 | "appearance" : "luminosity",
19 | "value" : "dark"
20 | }
21 | ],
22 | "color" : {
23 | "color-space" : "srgb",
24 | "components" : {
25 | "alpha" : "1.000",
26 | "blue" : "0x17",
27 | "green" : "0x17",
28 | "red" : "0x17"
29 | }
30 | },
31 | "idiom" : "universal"
32 | }
33 | ],
34 | "info" : {
35 | "author" : "xcode",
36 | "version" : 1
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/Projects/UI/Resources/Colors.xcassets/gray600.colorset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "colors" : [
3 | {
4 | "color" : {
5 | "color-space" : "srgb",
6 | "components" : {
7 | "alpha" : "0.600",
8 | "blue" : "0xE9",
9 | "green" : "0xE5",
10 | "red" : "0xE5"
11 | }
12 | },
13 | "idiom" : "universal"
14 | },
15 | {
16 | "appearances" : [
17 | {
18 | "appearance" : "luminosity",
19 | "value" : "dark"
20 | }
21 | ],
22 | "color" : {
23 | "color-space" : "srgb",
24 | "components" : {
25 | "alpha" : "0.600",
26 | "blue" : "0x2E",
27 | "green" : "0x2C",
28 | "red" : "0x2C"
29 | }
30 | },
31 | "idiom" : "universal"
32 | }
33 | ],
34 | "info" : {
35 | "author" : "xcode",
36 | "version" : 1
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/Projects/UI/Resources/Colors.xcassets/mainBlack.colorset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "colors" : [
3 | {
4 | "color" : {
5 | "color-space" : "srgb",
6 | "components" : {
7 | "alpha" : "1.000",
8 | "blue" : "0x00",
9 | "green" : "0x00",
10 | "red" : "0x00"
11 | }
12 | },
13 | "idiom" : "universal"
14 | },
15 | {
16 | "appearances" : [
17 | {
18 | "appearance" : "luminosity",
19 | "value" : "dark"
20 | }
21 | ],
22 | "color" : {
23 | "color-space" : "srgb",
24 | "components" : {
25 | "alpha" : "1.000",
26 | "blue" : "0xEE",
27 | "green" : "0xEA",
28 | "red" : "0xE9"
29 | }
30 | },
31 | "idiom" : "universal"
32 | }
33 | ],
34 | "info" : {
35 | "author" : "xcode",
36 | "version" : 1
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/Projects/UI/Resources/Colors.xcassets/mainOrange.colorset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "colors" : [
3 | {
4 | "color" : {
5 | "color-space" : "srgb",
6 | "components" : {
7 | "alpha" : "1.000",
8 | "blue" : "0x32",
9 | "green" : "0x64",
10 | "red" : "0xFA"
11 | }
12 | },
13 | "idiom" : "universal"
14 | },
15 | {
16 | "appearances" : [
17 | {
18 | "appearance" : "luminosity",
19 | "value" : "dark"
20 | }
21 | ],
22 | "color" : {
23 | "color-space" : "srgb",
24 | "components" : {
25 | "alpha" : "1.000",
26 | "blue" : "0x30",
27 | "green" : "0x5A",
28 | "red" : "0xD7"
29 | }
30 | },
31 | "idiom" : "universal"
32 | }
33 | ],
34 | "info" : {
35 | "author" : "xcode",
36 | "version" : 1
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/Projects/UI/Resources/Colors.xcassets/mainWhite.colorset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "colors" : [
3 | {
4 | "color" : {
5 | "color-space" : "srgb",
6 | "components" : {
7 | "alpha" : "1.000",
8 | "blue" : "0xFF",
9 | "green" : "0xFF",
10 | "red" : "0xFF"
11 | }
12 | },
13 | "idiom" : "universal"
14 | },
15 | {
16 | "appearances" : [
17 | {
18 | "appearance" : "luminosity",
19 | "value" : "dark"
20 | }
21 | ],
22 | "color" : {
23 | "color-space" : "srgb",
24 | "components" : {
25 | "alpha" : "1.000",
26 | "blue" : "0x1E",
27 | "green" : "0x1C",
28 | "red" : "0x1C"
29 | }
30 | },
31 | "idiom" : "universal"
32 | }
33 | ],
34 | "info" : {
35 | "author" : "xcode",
36 | "version" : 1
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/Projects/UI/Resources/Colors.xcassets/orange100.colorset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "colors" : [
3 | {
4 | "color" : {
5 | "color-space" : "srgb",
6 | "components" : {
7 | "alpha" : "0.100",
8 | "blue" : "0x32",
9 | "green" : "0x64",
10 | "red" : "0xFA"
11 | }
12 | },
13 | "idiom" : "universal"
14 | },
15 | {
16 | "appearances" : [
17 | {
18 | "appearance" : "luminosity",
19 | "value" : "dark"
20 | }
21 | ],
22 | "color" : {
23 | "color-space" : "srgb",
24 | "components" : {
25 | "alpha" : "0.100",
26 | "blue" : "0x30",
27 | "green" : "0x5A",
28 | "red" : "0xD7"
29 | }
30 | },
31 | "idiom" : "universal"
32 | }
33 | ],
34 | "info" : {
35 | "author" : "xcode",
36 | "version" : 1
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/Projects/UI/Resources/Colors.xcassets/shareBG.colorset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "colors" : [
3 | {
4 | "color" : {
5 | "color-space" : "srgb",
6 | "components" : {
7 | "alpha" : "1.000",
8 | "blue" : "0x17",
9 | "green" : "0x17",
10 | "red" : "0x17"
11 | }
12 | },
13 | "idiom" : "universal"
14 | },
15 | {
16 | "appearances" : [
17 | {
18 | "appearance" : "luminosity",
19 | "value" : "dark"
20 | }
21 | ],
22 | "color" : {
23 | "color-space" : "srgb",
24 | "components" : {
25 | "alpha" : "1.000",
26 | "blue" : "0x17",
27 | "green" : "0x17",
28 | "red" : "0x17"
29 | }
30 | },
31 | "idiom" : "universal"
32 | }
33 | ],
34 | "info" : {
35 | "author" : "xcode",
36 | "version" : 1
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/Projects/UI/Resources/Colors.xcassets/toast1.colorset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "colors" : [
3 | {
4 | "color" : {
5 | "color-space" : "srgb",
6 | "components" : {
7 | "alpha" : "1.000",
8 | "blue" : "0x6F",
9 | "green" : "0xFF",
10 | "red" : "0x9D"
11 | }
12 | },
13 | "idiom" : "universal"
14 | },
15 | {
16 | "appearances" : [
17 | {
18 | "appearance" : "luminosity",
19 | "value" : "dark"
20 | }
21 | ],
22 | "color" : {
23 | "color-space" : "srgb",
24 | "components" : {
25 | "alpha" : "1.000",
26 | "blue" : "0x41",
27 | "green" : "0xDE",
28 | "red" : "0x73"
29 | }
30 | },
31 | "idiom" : "universal"
32 | }
33 | ],
34 | "info" : {
35 | "author" : "xcode",
36 | "version" : 1
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/Projects/UI/Resources/Colors.xcassets/toast2.colorset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "colors" : [
3 | {
4 | "color" : {
5 | "color-space" : "srgb",
6 | "components" : {
7 | "alpha" : "1.000",
8 | "blue" : "0x32",
9 | "green" : "0x32",
10 | "red" : "0xFF"
11 | }
12 | },
13 | "idiom" : "universal"
14 | },
15 | {
16 | "appearances" : [
17 | {
18 | "appearance" : "luminosity",
19 | "value" : "dark"
20 | }
21 | ],
22 | "color" : {
23 | "color-space" : "srgb",
24 | "components" : {
25 | "alpha" : "1.000",
26 | "blue" : "0x25",
27 | "green" : "0x25",
28 | "red" : "0xDC"
29 | }
30 | },
31 | "idiom" : "universal"
32 | }
33 | ],
34 | "info" : {
35 | "author" : "xcode",
36 | "version" : 1
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/Projects/UI/Resources/Colors.xcassets/toastBG.colorset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "colors" : [
3 | {
4 | "color" : {
5 | "color-space" : "srgb",
6 | "components" : {
7 | "alpha" : "0.900",
8 | "blue" : "0x60",
9 | "green" : "0x60",
10 | "red" : "0x60"
11 | }
12 | },
13 | "idiom" : "universal"
14 | },
15 | {
16 | "appearances" : [
17 | {
18 | "appearance" : "luminosity",
19 | "value" : "dark"
20 | }
21 | ],
22 | "color" : {
23 | "color-space" : "srgb",
24 | "components" : {
25 | "alpha" : "0.900",
26 | "blue" : "0xDB",
27 | "green" : "0xDB",
28 | "red" : "0xDB"
29 | }
30 | },
31 | "idiom" : "universal"
32 | }
33 | ],
34 | "info" : {
35 | "author" : "xcode",
36 | "version" : 1
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/Projects/UI/Resources/Images.xcassets/AppImages/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/Projects/UI/Resources/Images.xcassets/AppImages/appleMusic.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "appleMusic.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 |
--------------------------------------------------------------------------------
/Projects/UI/Resources/Images.xcassets/AppImages/appleMusic.imageset/appleMusic.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DeveloperAcademy-POSTECH/2023-MacC-Team12-Creative8/4b68cf6d2cee5683693269560ffbd23162034ec0/Projects/UI/Resources/Images.xcassets/AppImages/appleMusic.imageset/appleMusic.png
--------------------------------------------------------------------------------
/Projects/UI/Resources/Images.xcassets/AppImages/spotify.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "spotify.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 |
--------------------------------------------------------------------------------
/Projects/UI/Resources/Images.xcassets/AppImages/spotify.imageset/spotify.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DeveloperAcademy-POSTECH/2023-MacC-Team12-Creative8/4b68cf6d2cee5683693269560ffbd23162034ec0/Projects/UI/Resources/Images.xcassets/AppImages/spotify.imageset/spotify.png
--------------------------------------------------------------------------------
/Projects/UI/Resources/Images.xcassets/AppImages/youtubeMusic.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "youtubeMusic.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 |
--------------------------------------------------------------------------------
/Projects/UI/Resources/Images.xcassets/AppImages/youtubeMusic.imageset/youtubeMusic.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DeveloperAcademy-POSTECH/2023-MacC-Team12-Creative8/4b68cf6d2cee5683693269560ffbd23162034ec0/Projects/UI/Resources/Images.xcassets/AppImages/youtubeMusic.imageset/youtubeMusic.png
--------------------------------------------------------------------------------
/Projects/UI/Resources/Images.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/Projects/UI/Resources/Images.xcassets/ForeignArtist/AJR.imageset/AJR.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DeveloperAcademy-POSTECH/2023-MacC-Team12-Creative8/4b68cf6d2cee5683693269560ffbd23162034ec0/Projects/UI/Resources/Images.xcassets/ForeignArtist/AJR.imageset/AJR.jpg
--------------------------------------------------------------------------------
/Projects/UI/Resources/Images.xcassets/ForeignArtist/AJR.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "AJR.jpg",
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 |
--------------------------------------------------------------------------------
/Projects/UI/Resources/Images.xcassets/ForeignArtist/BrunoMars.imageset/BrunoMars.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DeveloperAcademy-POSTECH/2023-MacC-Team12-Creative8/4b68cf6d2cee5683693269560ffbd23162034ec0/Projects/UI/Resources/Images.xcassets/ForeignArtist/BrunoMars.imageset/BrunoMars.jpg
--------------------------------------------------------------------------------
/Projects/UI/Resources/Images.xcassets/ForeignArtist/BrunoMars.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "BrunoMars.jpg",
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 |
--------------------------------------------------------------------------------
/Projects/UI/Resources/Images.xcassets/ForeignArtist/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/Projects/UI/Resources/Images.xcassets/ForeignArtist/DojaCat.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "DojaCat.jpg",
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 |
--------------------------------------------------------------------------------
/Projects/UI/Resources/Images.xcassets/ForeignArtist/DojaCat.imageset/DojaCat.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DeveloperAcademy-POSTECH/2023-MacC-Team12-Creative8/4b68cf6d2cee5683693269560ffbd23162034ec0/Projects/UI/Resources/Images.xcassets/ForeignArtist/DojaCat.imageset/DojaCat.jpg
--------------------------------------------------------------------------------
/Projects/UI/Resources/Images.xcassets/ForeignArtist/Drake.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "Drake.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 |
--------------------------------------------------------------------------------
/Projects/UI/Resources/Images.xcassets/ForeignArtist/Drake.imageset/Drake.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DeveloperAcademy-POSTECH/2023-MacC-Team12-Creative8/4b68cf6d2cee5683693269560ffbd23162034ec0/Projects/UI/Resources/Images.xcassets/ForeignArtist/Drake.imageset/Drake.png
--------------------------------------------------------------------------------
/Projects/UI/Resources/Images.xcassets/ForeignArtist/DuaLipa.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "DuaLipa.jpg",
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 |
--------------------------------------------------------------------------------
/Projects/UI/Resources/Images.xcassets/ForeignArtist/DuaLipa.imageset/DuaLipa.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DeveloperAcademy-POSTECH/2023-MacC-Team12-Creative8/4b68cf6d2cee5683693269560ffbd23162034ec0/Projects/UI/Resources/Images.xcassets/ForeignArtist/DuaLipa.imageset/DuaLipa.jpg
--------------------------------------------------------------------------------
/Projects/UI/Resources/Images.xcassets/ForeignArtist/Eminem.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "Eminem.jpg",
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 |
--------------------------------------------------------------------------------
/Projects/UI/Resources/Images.xcassets/ForeignArtist/Eminem.imageset/Eminem.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DeveloperAcademy-POSTECH/2023-MacC-Team12-Creative8/4b68cf6d2cee5683693269560ffbd23162034ec0/Projects/UI/Resources/Images.xcassets/ForeignArtist/Eminem.imageset/Eminem.jpg
--------------------------------------------------------------------------------
/Projects/UI/Resources/Images.xcassets/ForeignArtist/JohnK.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "JohnK.jpg",
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 |
--------------------------------------------------------------------------------
/Projects/UI/Resources/Images.xcassets/ForeignArtist/JohnK.imageset/JohnK.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DeveloperAcademy-POSTECH/2023-MacC-Team12-Creative8/4b68cf6d2cee5683693269560ffbd23162034ec0/Projects/UI/Resources/Images.xcassets/ForeignArtist/JohnK.imageset/JohnK.jpg
--------------------------------------------------------------------------------
/Projects/UI/Resources/Images.xcassets/ForeignArtist/Maroon5.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "Maroon5.jpg",
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 |
--------------------------------------------------------------------------------
/Projects/UI/Resources/Images.xcassets/ForeignArtist/Maroon5.imageset/Maroon5.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DeveloperAcademy-POSTECH/2023-MacC-Team12-Creative8/4b68cf6d2cee5683693269560ffbd23162034ec0/Projects/UI/Resources/Images.xcassets/ForeignArtist/Maroon5.imageset/Maroon5.jpg
--------------------------------------------------------------------------------
/Projects/UI/Resources/Images.xcassets/ForeignArtist/PostMalone.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "PostMalone.jpg",
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 |
--------------------------------------------------------------------------------
/Projects/UI/Resources/Images.xcassets/ForeignArtist/PostMalone.imageset/PostMalone.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DeveloperAcademy-POSTECH/2023-MacC-Team12-Creative8/4b68cf6d2cee5683693269560ffbd23162034ec0/Projects/UI/Resources/Images.xcassets/ForeignArtist/PostMalone.imageset/PostMalone.jpg
--------------------------------------------------------------------------------
/Projects/UI/Resources/Images.xcassets/ForeignArtist/SamSmith.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "SamSmith.jpg",
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 |
--------------------------------------------------------------------------------
/Projects/UI/Resources/Images.xcassets/ForeignArtist/SamSmith.imageset/SamSmith.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DeveloperAcademy-POSTECH/2023-MacC-Team12-Creative8/4b68cf6d2cee5683693269560ffbd23162034ec0/Projects/UI/Resources/Images.xcassets/ForeignArtist/SamSmith.imageset/SamSmith.jpg
--------------------------------------------------------------------------------
/Projects/UI/Resources/Images.xcassets/ForeignArtist/TheWeekend.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "TheWeekend.jpg",
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 |
--------------------------------------------------------------------------------
/Projects/UI/Resources/Images.xcassets/ForeignArtist/TheWeekend.imageset/TheWeekend.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DeveloperAcademy-POSTECH/2023-MacC-Team12-Creative8/4b68cf6d2cee5683693269560ffbd23162034ec0/Projects/UI/Resources/Images.xcassets/ForeignArtist/TheWeekend.imageset/TheWeekend.jpg
--------------------------------------------------------------------------------
/Projects/UI/Resources/Images.xcassets/ForeignArtist/TroyeSivan.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "TroyeSivan.jpg",
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 |
--------------------------------------------------------------------------------
/Projects/UI/Resources/Images.xcassets/ForeignArtist/TroyeSivan.imageset/TroyeSivan.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DeveloperAcademy-POSTECH/2023-MacC-Team12-Creative8/4b68cf6d2cee5683693269560ffbd23162034ec0/Projects/UI/Resources/Images.xcassets/ForeignArtist/TroyeSivan.imageset/TroyeSivan.jpg
--------------------------------------------------------------------------------
/Projects/UI/Resources/Images.xcassets/KoreanArtist/BTS.imageset/BTS.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DeveloperAcademy-POSTECH/2023-MacC-Team12-Creative8/4b68cf6d2cee5683693269560ffbd23162034ec0/Projects/UI/Resources/Images.xcassets/KoreanArtist/BTS.imageset/BTS.jpg
--------------------------------------------------------------------------------
/Projects/UI/Resources/Images.xcassets/KoreanArtist/BTS.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "BTS.jpg",
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 |
--------------------------------------------------------------------------------
/Projects/UI/Resources/Images.xcassets/KoreanArtist/CarTheGarden.imageset/CarTheGarden.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DeveloperAcademy-POSTECH/2023-MacC-Team12-Creative8/4b68cf6d2cee5683693269560ffbd23162034ec0/Projects/UI/Resources/Images.xcassets/KoreanArtist/CarTheGarden.imageset/CarTheGarden.jpg
--------------------------------------------------------------------------------
/Projects/UI/Resources/Images.xcassets/KoreanArtist/CarTheGarden.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "CarTheGarden.jpg",
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 |
--------------------------------------------------------------------------------
/Projects/UI/Resources/Images.xcassets/KoreanArtist/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/Projects/UI/Resources/Images.xcassets/KoreanArtist/DAY6.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "DAY6.jpg",
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 |
--------------------------------------------------------------------------------
/Projects/UI/Resources/Images.xcassets/KoreanArtist/DAY6.imageset/DAY6.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DeveloperAcademy-POSTECH/2023-MacC-Team12-Creative8/4b68cf6d2cee5683693269560ffbd23162034ec0/Projects/UI/Resources/Images.xcassets/KoreanArtist/DAY6.imageset/DAY6.jpg
--------------------------------------------------------------------------------
/Projects/UI/Resources/Images.xcassets/KoreanArtist/JayPark.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "JayPark.jpg",
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 |
--------------------------------------------------------------------------------
/Projects/UI/Resources/Images.xcassets/KoreanArtist/JayPark.imageset/JayPark.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DeveloperAcademy-POSTECH/2023-MacC-Team12-Creative8/4b68cf6d2cee5683693269560ffbd23162034ec0/Projects/UI/Resources/Images.xcassets/KoreanArtist/JayPark.imageset/JayPark.jpg
--------------------------------------------------------------------------------
/Projects/UI/Resources/Images.xcassets/KoreanArtist/LeeYoungJi.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "LeeYoungJi.jpg",
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 |
--------------------------------------------------------------------------------
/Projects/UI/Resources/Images.xcassets/KoreanArtist/LeeYoungJi.imageset/LeeYoungJi.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DeveloperAcademy-POSTECH/2023-MacC-Team12-Creative8/4b68cf6d2cee5683693269560ffbd23162034ec0/Projects/UI/Resources/Images.xcassets/KoreanArtist/LeeYoungJi.imageset/LeeYoungJi.jpg
--------------------------------------------------------------------------------
/Projects/UI/Resources/Images.xcassets/KoreanArtist/NCTDream.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "NCTDream.jpg",
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 |
--------------------------------------------------------------------------------
/Projects/UI/Resources/Images.xcassets/KoreanArtist/NCTDream.imageset/NCTDream.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DeveloperAcademy-POSTECH/2023-MacC-Team12-Creative8/4b68cf6d2cee5683693269560ffbd23162034ec0/Projects/UI/Resources/Images.xcassets/KoreanArtist/NCTDream.imageset/NCTDream.jpg
--------------------------------------------------------------------------------
/Projects/UI/Resources/Images.xcassets/KoreanArtist/NewJeans.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "NewJeans.jpg",
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 |
--------------------------------------------------------------------------------
/Projects/UI/Resources/Images.xcassets/KoreanArtist/NewJeans.imageset/NewJeans.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DeveloperAcademy-POSTECH/2023-MacC-Team12-Creative8/4b68cf6d2cee5683693269560ffbd23162034ec0/Projects/UI/Resources/Images.xcassets/KoreanArtist/NewJeans.imageset/NewJeans.jpg
--------------------------------------------------------------------------------
/Projects/UI/Resources/Images.xcassets/KoreanArtist/PaulKim.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "PaulKim.jpg",
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 |
--------------------------------------------------------------------------------
/Projects/UI/Resources/Images.xcassets/KoreanArtist/PaulKim.imageset/PaulKim.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DeveloperAcademy-POSTECH/2023-MacC-Team12-Creative8/4b68cf6d2cee5683693269560ffbd23162034ec0/Projects/UI/Resources/Images.xcassets/KoreanArtist/PaulKim.imageset/PaulKim.jpg
--------------------------------------------------------------------------------
/Projects/UI/Resources/Images.xcassets/KoreanArtist/StrayKids.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "StrayKids.jpg",
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 |
--------------------------------------------------------------------------------
/Projects/UI/Resources/Images.xcassets/KoreanArtist/StrayKids.imageset/StrayKids.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DeveloperAcademy-POSTECH/2023-MacC-Team12-Creative8/4b68cf6d2cee5683693269560ffbd23162034ec0/Projects/UI/Resources/Images.xcassets/KoreanArtist/StrayKids.imageset/StrayKids.jpg
--------------------------------------------------------------------------------
/Projects/UI/Resources/Images.xcassets/KoreanArtist/Woodz.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "Woodz.jpg",
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 |
--------------------------------------------------------------------------------
/Projects/UI/Resources/Images.xcassets/KoreanArtist/Woodz.imageset/Woodz.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DeveloperAcademy-POSTECH/2023-MacC-Team12-Creative8/4b68cf6d2cee5683693269560ffbd23162034ec0/Projects/UI/Resources/Images.xcassets/KoreanArtist/Woodz.imageset/Woodz.jpg
--------------------------------------------------------------------------------
/Projects/UI/Resources/Images.xcassets/KoreanArtist/Yoonha.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "Yoonha.jpg",
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 |
--------------------------------------------------------------------------------
/Projects/UI/Resources/Images.xcassets/KoreanArtist/Yoonha.imageset/Yoonha.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DeveloperAcademy-POSTECH/2023-MacC-Team12-Creative8/4b68cf6d2cee5683693269560ffbd23162034ec0/Projects/UI/Resources/Images.xcassets/KoreanArtist/Yoonha.imageset/Yoonha.jpg
--------------------------------------------------------------------------------
/Projects/UI/Resources/Images.xcassets/archiveIcon.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "archiveIcon.svg",
5 | "idiom" : "universal"
6 | }
7 | ],
8 | "info" : {
9 | "author" : "xcode",
10 | "version" : 1
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/Projects/UI/Resources/Images.xcassets/archiveIcon.imageset/archiveIcon.svg:
--------------------------------------------------------------------------------
1 |
11 |
--------------------------------------------------------------------------------
/Projects/UI/Resources/Images.xcassets/artistViewTicket.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "appearances" : [
9 | {
10 | "appearance" : "luminosity",
11 | "value" : "dark"
12 | }
13 | ],
14 | "idiom" : "universal",
15 | "scale" : "1x"
16 | },
17 | {
18 | "idiom" : "universal",
19 | "scale" : "2x"
20 | },
21 | {
22 | "appearances" : [
23 | {
24 | "appearance" : "luminosity",
25 | "value" : "dark"
26 | }
27 | ],
28 | "idiom" : "universal",
29 | "scale" : "2x"
30 | },
31 | {
32 | "filename" : "Group 1000002051.png",
33 | "idiom" : "universal",
34 | "scale" : "3x"
35 | },
36 | {
37 | "appearances" : [
38 | {
39 | "appearance" : "luminosity",
40 | "value" : "dark"
41 | }
42 | ],
43 | "filename" : "darkArtistViewTicket.png",
44 | "idiom" : "universal",
45 | "scale" : "3x"
46 | }
47 | ],
48 | "info" : {
49 | "author" : "xcode",
50 | "version" : 1
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/Projects/UI/Resources/Images.xcassets/artistViewTicket.imageset/Group 1000002051.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DeveloperAcademy-POSTECH/2023-MacC-Team12-Creative8/4b68cf6d2cee5683693269560ffbd23162034ec0/Projects/UI/Resources/Images.xcassets/artistViewTicket.imageset/Group 1000002051.png
--------------------------------------------------------------------------------
/Projects/UI/Resources/Images.xcassets/artistViewTicket.imageset/darkArtistViewTicket.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DeveloperAcademy-POSTECH/2023-MacC-Team12-Creative8/4b68cf6d2cee5683693269560ffbd23162034ec0/Projects/UI/Resources/Images.xcassets/artistViewTicket.imageset/darkArtistViewTicket.png
--------------------------------------------------------------------------------
/Projects/UI/Resources/Images.xcassets/darkArtistViewTicket.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "darkArtistViewTicket.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 |
--------------------------------------------------------------------------------
/Projects/UI/Resources/Images.xcassets/darkArtistViewTicket.imageset/darkArtistViewTicket.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DeveloperAcademy-POSTECH/2023-MacC-Team12-Creative8/4b68cf6d2cee5683693269560ffbd23162034ec0/Projects/UI/Resources/Images.xcassets/darkArtistViewTicket.imageset/darkArtistViewTicket.png
--------------------------------------------------------------------------------
/Projects/UI/Resources/Images.xcassets/darkTicket.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "darkTicket.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 |
--------------------------------------------------------------------------------
/Projects/UI/Resources/Images.xcassets/darkTicket.imageset/darkTicket.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DeveloperAcademy-POSTECH/2023-MacC-Team12-Creative8/4b68cf6d2cee5683693269560ffbd23162034ec0/Projects/UI/Resources/Images.xcassets/darkTicket.imageset/darkTicket.png
--------------------------------------------------------------------------------
/Projects/UI/Resources/Images.xcassets/homeIcon.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "homeIcon.svg",
5 | "idiom" : "universal"
6 | }
7 | ],
8 | "info" : {
9 | "author" : "xcode",
10 | "version" : 1
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/Projects/UI/Resources/Images.xcassets/homeIcon.imageset/homeIcon.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/Projects/UI/Resources/Images.xcassets/mainViewTicket.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "Ticket.svg",
5 | "idiom" : "universal"
6 | }
7 | ],
8 | "info" : {
9 | "author" : "xcode",
10 | "version" : 1
11 | },
12 | "properties" : {
13 | "template-rendering-intent" : "template"
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/Projects/UI/Resources/Images.xcassets/mainViewTicket.imageset/Ticket.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/Projects/UI/Resources/Images.xcassets/modalCloseButton.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "modal_close_button.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 |
--------------------------------------------------------------------------------
/Projects/UI/Resources/Images.xcassets/modalCloseButton.imageset/modal_close_button.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DeveloperAcademy-POSTECH/2023-MacC-Team12-Creative8/4b68cf6d2cee5683693269560ffbd23162034ec0/Projects/UI/Resources/Images.xcassets/modalCloseButton.imageset/modal_close_button.png
--------------------------------------------------------------------------------
/Projects/UI/Resources/Images.xcassets/screenshotforOCR.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "screenshotforOCR.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 |
--------------------------------------------------------------------------------
/Projects/UI/Resources/Images.xcassets/screenshotforOCR.imageset/screenshotforOCR.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DeveloperAcademy-POSTECH/2023-MacC-Team12-Creative8/4b68cf6d2cee5683693269560ffbd23162034ec0/Projects/UI/Resources/Images.xcassets/screenshotforOCR.imageset/screenshotforOCR.png
--------------------------------------------------------------------------------
/Projects/UI/Resources/Images.xcassets/searchIcon.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "searchIcon.svg",
5 | "idiom" : "universal"
6 | }
7 | ],
8 | "info" : {
9 | "author" : "xcode",
10 | "version" : 1
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/Projects/UI/Resources/Images.xcassets/searchIcon.imageset/searchIcon.svg:
--------------------------------------------------------------------------------
1 |
12 |
--------------------------------------------------------------------------------
/Projects/UI/Resources/Images.xcassets/serviceForConcertgoers.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "serviceForConcertgoers.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 |
--------------------------------------------------------------------------------
/Projects/UI/Resources/Images.xcassets/serviceForConcertgoers.imageset/serviceForConcertgoers.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DeveloperAcademy-POSTECH/2023-MacC-Team12-Creative8/4b68cf6d2cee5683693269560ffbd23162034ec0/Projects/UI/Resources/Images.xcassets/serviceForConcertgoers.imageset/serviceForConcertgoers.png
--------------------------------------------------------------------------------
/Projects/UI/Resources/Images.xcassets/seta.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "universal",
9 | "scale" : "2x"
10 | },
11 | {
12 | "filename" : "Seta.svg",
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "author" : "xcode",
19 | "version" : 1
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/Projects/UI/Resources/Images.xcassets/seta.imageset/Seta.svg:
--------------------------------------------------------------------------------
1 |
7 |
--------------------------------------------------------------------------------
/Projects/UI/Resources/Images.xcassets/ticket.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "ticket.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 |
--------------------------------------------------------------------------------
/Projects/UI/Resources/Images.xcassets/ticket.imageset/ticket.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DeveloperAcademy-POSTECH/2023-MacC-Team12-Creative8/4b68cf6d2cee5683693269560ffbd23162034ec0/Projects/UI/Resources/Images.xcassets/ticket.imageset/ticket.png
--------------------------------------------------------------------------------
/Projects/UI/Resources/Images.xcassets/whiteTicket.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "whiteTicket.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 |
--------------------------------------------------------------------------------
/Projects/UI/Resources/Images.xcassets/whiteTicket.imageset/whiteTicket.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DeveloperAcademy-POSTECH/2023-MacC-Team12-Creative8/4b68cf6d2cee5683693269560ffbd23162034ec0/Projects/UI/Resources/Images.xcassets/whiteTicket.imageset/whiteTicket.png
--------------------------------------------------------------------------------
/Projects/UI/Sources/Extensions/ArchiveExtension.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ArchiveExtension.swift
3 | // UI
4 | //
5 | // Created by A_Mcflurry on 10/22/23.
6 | // Copyright © 2023 com.creative8.seta. All rights reserved.
7 | //
8 |
9 | import SwiftUI
10 |
--------------------------------------------------------------------------------
/Projects/UI/Sources/Extensions/CellFrameExtension.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CellFrameExtension.swift
3 | // UI
4 | //
5 | // Created by A_Mcflurry on 10/17/23.
6 | // Copyright © 2023 com.creative8. All rights reserved.
7 | //
8 |
9 | import SwiftUI
10 |
11 | public extension View {
12 | func frameForCell() -> some View {
13 | self.frame(width: getCellFrame(), height: getCellFrame())
14 | }
15 | private func getCellFrame() -> CGFloat {
16 | return 56
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/Projects/UI/Sources/Extensions/ColorExtension.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Color+.swift
3 | // UI
4 | //
5 | // Created by 고혜지 on 10/15/23.
6 | // Copyright © 2023 com.creative8. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import SwiftUI
11 |
12 | public extension Color {
13 | init(hex: Int, opacity: Double = 1.0) {
14 | let red = Double((hex >> 16) & 0xff) / 255
15 | let green = Double((hex >> 8) & 0xff) / 255
16 | let blue = Double((hex >> 0) & 0xff) / 255
17 |
18 | self.init(.sRGB, red: red, green: green, blue: blue, opacity: opacity)
19 | }
20 | }
21 |
22 | public let setaBundle = Bundle(identifier: "com.creative8.seta.UI")
23 | public extension Color {
24 | static var mainBlack: Color {
25 | Color("mainBlack", bundle: setaBundle)
26 | }
27 |
28 | static var mainOrange: Color {
29 | Color("mainOrange", bundle: setaBundle)
30 | }
31 |
32 | static var mainWhite: Color {
33 | Color("mainWhite", bundle: setaBundle)
34 | }
35 |
36 | static var gray6: Color {
37 | Color("gray6", bundle: setaBundle)
38 | }
39 |
40 | static var orange100: Color {
41 | Color("orange100", bundle: setaBundle)
42 | }
43 |
44 | static var toast1: Color {
45 | Color("toast1", bundle: setaBundle)
46 | }
47 |
48 | static var toast2: Color {
49 | Color("toast2", bundle: setaBundle)
50 | }
51 |
52 | static var ellipsis: Color {
53 | Color("ellipsis", bundle: setaBundle)
54 | }
55 |
56 | static var gray600: Color {
57 | Color("gray600", bundle: setaBundle)
58 | }
59 |
60 | static var toastBG: Color {
61 | Color("toastBG", bundle: setaBundle)
62 | }
63 |
64 | static var shareBG: Color {
65 | Color("shareBG", bundle: setaBundle)
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/Projects/UI/Sources/Extensions/DateFormatterExtension.swift:
--------------------------------------------------------------------------------
1 | //
2 | // DateFormaterExtension.swift
3 | // UI
4 | //
5 | // Created by A_Mcflurry on 10/9/23.
6 | // Copyright © 2023 com.creative8. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | public extension DateFormatter {
12 | static func dateMonthFormatter() -> DateFormatter {
13 | let formatter = DateFormatter()
14 | formatter.dateFormat = "MM.dd"
15 | return formatter
16 | }
17 |
18 | static func yearFormatter() -> DateFormatter {
19 | let formatter = DateFormatter()
20 | formatter.locale = Locale(identifier: "ko_KR")
21 | formatter.dateFormat = "YYYY"
22 | return formatter
23 | }
24 |
25 | static func dateFormatter() -> DateFormatter {
26 | let formatter = DateFormatter()
27 | formatter.locale = Locale(identifier: "ko_KR")
28 | formatter.dateFormat = "YYYY년 MM월 dd일"
29 | return formatter
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/Projects/UI/Sources/Extensions/ImageCropExtension.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ImageCropExtension.swift
3 | // UI
4 | //
5 | // Created by A_Mcflurry on 10/17/23.
6 | // Copyright © 2023 com.creative8. All rights reserved.
7 | //
8 |
9 | import SwiftUI
10 |
11 | public extension Image {
12 | func centerCropped() -> some View {
13 | GeometryReader { geo in
14 | self
15 | .resizable()
16 | .scaledToFill()
17 | .frame(width: geo.size.width, height: geo.size.height)
18 | .clipped()
19 | }
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/Projects/UI/Sources/Extensions/UIScreenExtension.swift:
--------------------------------------------------------------------------------
1 | //
2 | // UIScreenExtension.swift
3 | // UI
4 | //
5 | // Created by A_Mcflurry on 10/14/23.
6 | // Copyright © 2023 com.creative8. All rights reserved.
7 | //
8 |
9 | import SwiftUI
10 |
11 | public extension View {
12 | var UIWidth: CGFloat {
13 | return UIScreen.main.bounds.width
14 | }
15 |
16 | var UIHeight: CGFloat {
17 | return UIScreen.main.bounds.height
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/Projects/UI/UI.xcodeproj/xcuserdata/a_mcflurry.xcuserdatad/xcschemes/xcschememanagement.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | SchemeUserState
6 |
7 | UI.xcscheme_^#shared#^_
8 |
9 | isShown
10 |
11 | orderHint
12 | 4
13 |
14 | UITests.xcscheme_^#shared#^_
15 |
16 | isShown
17 |
18 | orderHint
19 | 5
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/Projects/UI/UI.xcodeproj/xcuserdata/choihyowon.xcuserdatad/xcschemes/xcschememanagement.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | SchemeUserState
6 |
7 | UI.xcscheme_^#shared#^_
8 |
9 | isShown
10 |
11 | orderHint
12 | 4
13 |
14 | UITests.xcscheme_^#shared#^_
15 |
16 | isShown
17 |
18 | orderHint
19 | 5
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/Projects/UI/UI.xcodeproj/xcuserdata/kohyeji.xcuserdatad/xcschemes/xcschememanagement.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | SchemeUserState
6 |
7 | UI.xcscheme_^#shared#^_
8 |
9 | isShown
10 |
11 | orderHint
12 | 4
13 |
14 | UITests.xcscheme_^#shared#^_
15 |
16 | isShown
17 |
18 | orderHint
19 | 5
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/Projects/UI/UI.xcodeproj/xcuserdata/yeseul.xcuserdatad/xcschemes/xcschememanagement.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | SchemeUserState
6 |
7 | UI.xcscheme_^#shared#^_
8 |
9 | isShown
10 |
11 | orderHint
12 | 4
13 |
14 | UITests.xcscheme_^#shared#^_
15 |
16 | isShown
17 |
18 | orderHint
19 | 5
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/Setlist.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
7 |
9 |
10 |
12 |
13 |
15 |
16 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/Setlist.xcworkspace/xcuserdata/a_mcflurry.xcuserdatad/UserInterfaceState.xcuserstate:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DeveloperAcademy-POSTECH/2023-MacC-Team12-Creative8/4b68cf6d2cee5683693269560ffbd23162034ec0/Setlist.xcworkspace/xcuserdata/a_mcflurry.xcuserdatad/UserInterfaceState.xcuserstate
--------------------------------------------------------------------------------
/Setlist.xcworkspace/xcuserdata/a_mcflurry.xcuserdatad/xcschemes/xcschememanagement.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | SchemeUserState
6 |
7 | Promises (Playground) 1.xcscheme
8 |
9 | isShown
10 |
11 | orderHint
12 | 7
13 |
14 | Promises (Playground) 2.xcscheme
15 |
16 | isShown
17 |
18 | orderHint
19 | 8
20 |
21 | Promises (Playground).xcscheme
22 |
23 | isShown
24 |
25 | orderHint
26 | 6
27 |
28 | Setlist-Workspace.xcscheme_^#shared#^_
29 |
30 | isShown
31 |
32 | orderHint
33 | 3
34 |
35 |
36 |
37 |
38 |
--------------------------------------------------------------------------------
/Setlist.xcworkspace/xcuserdata/choihyowon.xcuserdatad/UserInterfaceState.xcuserstate:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DeveloperAcademy-POSTECH/2023-MacC-Team12-Creative8/4b68cf6d2cee5683693269560ffbd23162034ec0/Setlist.xcworkspace/xcuserdata/choihyowon.xcuserdatad/UserInterfaceState.xcuserstate
--------------------------------------------------------------------------------
/Setlist.xcworkspace/xcuserdata/kohyeji.xcuserdatad/UserInterfaceState.xcuserstate:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DeveloperAcademy-POSTECH/2023-MacC-Team12-Creative8/4b68cf6d2cee5683693269560ffbd23162034ec0/Setlist.xcworkspace/xcuserdata/kohyeji.xcuserdatad/UserInterfaceState.xcuserstate
--------------------------------------------------------------------------------
/Setlist.xcworkspace/xcuserdata/kohyeji.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
9 |
16 |
17 |
18 |
19 |
20 |
22 |
34 |
35 |
36 |
37 |
38 |
--------------------------------------------------------------------------------
/Setlist.xcworkspace/xcuserdata/kohyeji.xcuserdatad/xcschemes/xcschememanagement.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | SchemeUserState
6 |
7 | Promises (Playground) 1.xcscheme
8 |
9 | isShown
10 |
11 | orderHint
12 | 10
13 |
14 | Promises (Playground) 2.xcscheme
15 |
16 | isShown
17 |
18 | orderHint
19 | 11
20 |
21 | Promises (Playground).xcscheme
22 |
23 | isShown
24 |
25 | orderHint
26 | 9
27 |
28 | Setlist-Workspace.xcscheme_^#shared#^_
29 |
30 | isShown
31 |
32 | orderHint
33 | 3
34 |
35 |
36 |
37 |
38 |
--------------------------------------------------------------------------------
/Setlist.xcworkspace/xcuserdata/yeseul.xcuserdatad/UserInterfaceState.xcuserstate:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DeveloperAcademy-POSTECH/2023-MacC-Team12-Creative8/4b68cf6d2cee5683693269560ffbd23162034ec0/Setlist.xcworkspace/xcuserdata/yeseul.xcuserdatad/UserInterfaceState.xcuserstate
--------------------------------------------------------------------------------
/Setlist.xcworkspace/xcuserdata/yeseul.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
9 |
21 |
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/Setlist.xcworkspace/xcuserdata/yeseul.xcuserdatad/xcschemes/xcschememanagement.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | SchemeUserState
6 |
7 | Promises (Playground) 1.xcscheme
8 |
9 | isShown
10 |
11 | orderHint
12 | 11
13 |
14 | Promises (Playground) 2.xcscheme
15 |
16 | isShown
17 |
18 | orderHint
19 | 12
20 |
21 | Promises (Playground).xcscheme
22 |
23 | isShown
24 |
25 | orderHint
26 | 10
27 |
28 | Setlist-Workspace.xcscheme_^#shared#^_
29 |
30 | isShown
31 |
32 | orderHint
33 | 3
34 |
35 |
36 |
37 |
38 |
--------------------------------------------------------------------------------
/Tuist/Config.swift:
--------------------------------------------------------------------------------
1 | import ProjectDescription
2 |
3 | let config = Config(
4 |
5 | )
6 |
--------------------------------------------------------------------------------
/Tuist/ProjectDescriptionHelpers/Dependency+Project.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Dependency+Project.swift
3 | // ProjectDescriptionHelpers
4 | //
5 | // Created by 최효원 on 2023/10/06.
6 | //
7 |
8 | import ProjectDescription
9 |
10 | extension TargetDependency {
11 | public enum Projcet {}
12 | }
13 |
14 | public extension TargetDependency.Projcet {
15 | static let Feature = TargetDependency.project(target: "Feature", path: .relativeToRoot("Projects/Feature"))
16 | static let Core = TargetDependency.project(target: "Core", path: .relativeToRoot("Projects/Core"))
17 | static let UI = TargetDependency.project(target: "UI", path: .relativeToRoot("Projects/UI"))
18 | }
19 |
--------------------------------------------------------------------------------
/Tuist/ProjectDescriptionHelpers/Dependency+Spm.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Dependency+Spm.swift
3 | // ProjectDescriptionHelpers
4 | //
5 | // Created by 최효원 on 2023/10/06.
6 | //
7 |
8 | import ProjectDescription
9 |
10 | public extension TargetDependency {
11 | enum SPM {}
12 | }
13 |
14 | public extension Package {
15 | static let MarqueeText = Package.remote(url: "https://github.com/joekndy/MarqueeText.git", requirement: .branch("master"))
16 | static let SpotifyAPI = Package.remote(url: "https://github.com/Peter-Schorn/SpotifyAPI.git", requirement: .upToNextMajor(from: "3.0.0"))
17 | static let KeychainAccess = Package.remote(url: "https://github.com/kishikawakatsumi/KeychainAccess.git", requirement: .upToNextMajor(from: "4.2.2"))
18 | static let Firebase = Package.remote(url: "https://github.com/firebase/firebase-ios-sdk.git", requirement: .upToNextMajor(from: "10.17.0"))
19 | }
20 |
21 | public extension TargetDependency.SPM {
22 | static let Firebase = TargetDependency.package(product: "FirebaseAnalytics")
23 | static let MarqueeText = TargetDependency.package(product: "MarqueeText")
24 | static let SpotifyAPI = TargetDependency.package(product: "SpotifyAPI")
25 | static let KeychainAccess = TargetDependency.package(product: "KeychainAccess")
26 | }
27 |
--------------------------------------------------------------------------------
/Tuist/ProjectDescriptionHelpers/Project+Templates.swift:
--------------------------------------------------------------------------------
1 | import ProjectDescription
2 |
3 | public extension Project {
4 | static func makeModule(
5 | name: String,
6 | platform: Platform = .iOS,
7 | product: Product,
8 | organizationName: String = "com.creative8.seta",
9 | packages: [Package] = [],
10 | deploymentTarget: DeploymentTarget? = .iOS(targetVersion: "17.0", devices: [.iphone]),
11 | dependencies: [TargetDependency] = [
12 | ],
13 | sources: SourceFilesList? = nil,
14 | resources: ResourceFileElements? = nil,
15 | infoPlist: InfoPlist = .default
16 | ) -> Project {
17 | let settings: Settings = .settings(
18 | base: [:],
19 | configurations: [
20 | .debug(name: .debug),
21 | .release(name: .release)
22 | ], defaultSettings: .recommended)
23 |
24 | let appTarget = Target(
25 | name: name,
26 | platform: platform,
27 | product: product,
28 | bundleId: "\(organizationName).\(name)",
29 | deploymentTarget: deploymentTarget,
30 | infoPlist: infoPlist,
31 | sources: sources,
32 | resources: resources,
33 | dependencies: dependencies
34 | )
35 |
36 | let testTarget = Target(
37 | name: "\(name)Tests",
38 | platform: platform,
39 | product: .unitTests,
40 | bundleId: "\(organizationName).\(name)Tests",
41 | deploymentTarget: deploymentTarget,
42 | infoPlist: .default,
43 | dependencies: [.target(name: name)]
44 | )
45 |
46 | let schemes: [Scheme] = [.makeScheme(target: .debug, name: name)]
47 |
48 | let targets: [Target] = [appTarget, testTarget]
49 |
50 | return Project(
51 | name: name,
52 | organizationName: organizationName,
53 | packages: packages,
54 | settings: settings,
55 | targets: targets,
56 | schemes: schemes
57 | )
58 | }
59 | }
60 |
61 | extension Scheme {
62 | static func makeScheme(target: ConfigurationName, name: String) -> Scheme {
63 | return Scheme(
64 | name: name,
65 | shared: true,
66 | buildAction: .buildAction(targets: ["\(name)"]),
67 | testAction: .targets(
68 | ["\(name)Tests"],
69 | configuration: target,
70 | options: .options(coverage: true, codeCoverageTargets: ["\(name)"])
71 | ),
72 | runAction: .runAction(configuration: target),
73 | archiveAction: .archiveAction(configuration: target),
74 | profileAction: .profileAction(configuration: target),
75 | analyzeAction: .analyzeAction(configuration: target)
76 | )
77 | }
78 | }
79 |
--------------------------------------------------------------------------------
/Workspace.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Workspace.swift
3 | // ProjectDescriptionHelpers
4 | //
5 | // Created by 최효원 on 2023/10/06.
6 | //
7 |
8 | import ProjectDescription
9 |
10 | let workspace = Workspace(
11 | name: "Setlist",
12 | projects: [
13 | "Projects/App"
14 | ]
15 | )
16 |
--------------------------------------------------------------------------------