diff --git a/.gitignore b/.gitignore index ae7b5f9..6453281 100644 --- a/.gitignore +++ b/.gitignore @@ -104,3 +104,4 @@ Derived/ *.xcworkspace *.xcodeproj master.key +Package.resolved diff --git a/.mise.toml b/.mise.toml index 6770e5b..10c31de 100644 --- a/.mise.toml +++ b/.mise.toml @@ -1,3 +1,3 @@ [tools] -tuist = "4.9.0" +tuist = "4.32.0" diff --git a/Projects/App/Sources/Application/GPleApp.swift b/Projects/App/Sources/Application/GPleApp.swift index 8328367..274c191 100644 --- a/Projects/App/Sources/Application/GPleApp.swift +++ b/Projects/App/Sources/Application/GPleApp.swift @@ -4,7 +4,7 @@ import SwiftUI struct GPleApp: App { var body: some Scene { WindowGroup { - MainView(postViewModel: PostViewModel()) + SplashView() } } } diff --git a/Projects/App/Sources/Feature/DetailFeature/Sources/DetailView.swift b/Projects/App/Sources/Feature/DetailFeature/Sources/DetailView.swift index bff29d4..9a2d8ae 100644 --- a/Projects/App/Sources/Feature/DetailFeature/Sources/DetailView.swift +++ b/Projects/App/Sources/Feature/DetailFeature/Sources/DetailView.swift @@ -1,12 +1,9 @@ import SwiftUI struct DetailView: View { - @StateObject var viewModel: DetailViewModel @State private var topNavigationState: Bool = false @State private var emojiName: [String] = ["heart", "congrats", "thumbsUp", "thinking", "poop", "china"] @State private var emojiServerName: [String] = ["HEART", "CONGRATUATION", "THUMBSUP", "THINKING", "POOP", "CHINA"] - @State private var emojiStates: [Int] = [0, 2, 3, 400, 500, 600] - @State private var test: [Bool] = [false, false, false, false, false, false] @State private var graySmileState: Bool = false @Environment(\.dismiss) private var dismiss @StateObject var postViewModel: PostViewModel @@ -23,6 +20,7 @@ struct DetailView: View { @State public var checkEmojiList: [Bool] = [] @State public var createTime: String = "" @State public var topNavigationBar: Bool = true + @State private var selectedIndex = 0 var body: some View { NavigationStack { @@ -66,12 +64,11 @@ struct DetailView: View { .font(GPleFontFamily.Pretendard.regular.swiftUIFont(size: 14)) .foregroundStyle(GPleAsset.Color.gray800.swiftUIColor) - Spacer() } .padding(.top, 8) - TabView(selection: $viewModel.imageCount) { + TabView(selection: $selectedIndex) { ForEach(imageUrl.indices, id: \.self) { index in if let imageUrl = URL(string: imageUrl[index]) { AsyncImage(url: imageUrl) { image in @@ -96,7 +93,6 @@ struct DetailView: View { .padding(.top, 16) .padding(.leading, 16) - HStack(spacing: 8) { ForEach(tagList.indices, id: \.self) { tag in Text("@\(tagList[tag].name)") @@ -107,7 +103,6 @@ struct DetailView: View { .padding(.top, 6) .padding(.leading, 16) - let dateString = createTime.split(separator: "T").first if let dateString = dateString { let components = dateString.split(separator: "-") @@ -155,12 +150,14 @@ struct DetailView: View { postViewModel.postEmoji { success in print("\(emojiName[tag]) 성공") - if checkEmojiList[tag] == true { - emojiList[tag] -= 1 - checkEmojiList[tag].toggle() - } else { - emojiList[tag] = 1 - checkEmojiList[tag].toggle() + if success { + if checkEmojiList[tag] == true { + emojiList[tag] -= 1 + checkEmojiList[tag].toggle() + } else { + emojiList[tag] = 1 + checkEmojiList[tag].toggle() + } } } }) { @@ -177,11 +174,9 @@ struct DetailView: View { .padding(.leading, 20) .padding(.top, 10) } - } } - Spacer() } .padding(.top, 8) diff --git a/Projects/App/Sources/Feature/DetailFeature/Sources/DetailViewModel.swift b/Projects/App/Sources/Feature/DetailFeature/Sources/DetailViewModel.swift deleted file mode 100644 index 763d72c..0000000 --- a/Projects/App/Sources/Feature/DetailFeature/Sources/DetailViewModel.swift +++ /dev/null @@ -1,11 +0,0 @@ -import Foundation - -final class DetailViewModel: ObservableObject { - @Published var name: String = "한재형" - @Published var schoolYear: Int = 2 - @Published var imageCount: Int = 3 - @Published var title: String = "유성이 없이 찍은 7기" - @Published var tagUser: [String] = ["박미리", "장예슬"] - @Published var wwDay: String = "6" - @Published var ddDay: String = "7" -} diff --git a/Projects/App/Sources/Feature/MainFeature/Sources/LocationPostView.swift b/Projects/App/Sources/Feature/MainFeature/Sources/LocationPostView.swift index 0fe8a20..7a442cb 100644 --- a/Projects/App/Sources/Feature/MainFeature/Sources/LocationPostView.swift +++ b/Projects/App/Sources/Feature/MainFeature/Sources/LocationPostView.swift @@ -34,7 +34,6 @@ struct LocationPostView: View { case "GYM": ForEach(viewModel.gymPostList) { post in DetailView( - viewModel: DetailViewModel(), postViewModel: PostViewModel(), postId: post.id, title: post.title, @@ -62,7 +61,6 @@ struct LocationPostView: View { case "HOME": ForEach(viewModel.homePostList) { post in DetailView( - viewModel: DetailViewModel(), postViewModel: PostViewModel(), postId: post.id, title: post.title, @@ -90,7 +88,6 @@ struct LocationPostView: View { case "PLAYGROUND": ForEach(viewModel.playgroundPostList) { post in DetailView( - viewModel: DetailViewModel(), postViewModel: PostViewModel(), postId: post.id, title: post.title, @@ -118,7 +115,6 @@ struct LocationPostView: View { case "DOMITORY": ForEach(viewModel.domitoryPostList) { post in DetailView( - viewModel: DetailViewModel(), postViewModel: PostViewModel(), postId: post.id, title: post.title, @@ -146,7 +142,6 @@ struct LocationPostView: View { case "WALKING_TRAIL": ForEach(viewModel.walkingTrailPostList) { post in DetailView( - viewModel: DetailViewModel(), postViewModel: PostViewModel(), postId: post.id, title: post.title, diff --git a/Projects/App/Sources/Feature/MainFeature/Sources/MainView.swift b/Projects/App/Sources/Feature/MainFeature/Sources/MainView.swift index 2bc66c6..90eb7a9 100644 --- a/Projects/App/Sources/Feature/MainFeature/Sources/MainView.swift +++ b/Projects/App/Sources/Feature/MainFeature/Sources/MainView.swift @@ -3,7 +3,7 @@ import SwiftUI struct MainView: View { @StateObject var viewModel = MainViewModel() @StateObject var postViewModel: PostViewModel - + var body: some View { NavigationView { NavigationStack { @@ -19,10 +19,12 @@ struct MainView: View { Spacer() - NavigationLink(destination: MyPageView(viewModel: MyPageViewModel(), postViewModel: PostViewModel())) { - GPleAsset.Assets.profile.swiftUIImage - .padding(.trailing, 20) - } + NavigationLink(destination: + MyPageView( + postViewModel: PostViewModel())) { + GPleAsset.Assets.profile.swiftUIImage + .padding(.trailing, 20) + } } .padding(.top, 16) @@ -64,15 +66,36 @@ struct MainView: View { .padding(.top, 16) ForEach(viewModel.allPostList) { post in - postList( + NavigationLink(destination: DetailView( + postViewModel: PostViewModel(), + postId: post.id, + location: post.location, + title: post.title, name: post.author.name, grade: post.author.grade, - title: post.title, - place: post.location, - tag: post.tagList.map { $0.name }, - date: post.createdTime, - imageURL: post.imageUrl - ) + imageUrl: post.imageUrl, + tagList: post.tagList.map { ($0.name, $0.id) }, + emojiList: [ + post.emojiList.heartCount, + post.emojiList.congCount, + post.emojiList.thumbsCount, + post.emojiList.thinkCount, + post.emojiList.poopCount, + post.emojiList.chinaCount + ], + checkEmojiList: post.checkEmoji, + createTime: post.createdTime + )) { + postList( + name: post.author.name, + grade: post.author.grade, + title: post.title, + place: post.location, + tag: post.tagList.map { $0.name }, + date: post.createdTime, + imageURL: post.imageUrl + ) + } } .padding(.top, 60) @@ -87,118 +110,118 @@ struct MainView: View { } } - @ViewBuilder - func rankButton() -> some View { - HStack { - GPleAsset.Assets.chart.swiftUIImage + @ViewBuilder + func rankButton() -> some View { + HStack { + GPleAsset.Assets.chart.swiftUIImage - Text("인기 순위") - .foregroundStyle(GPleAsset.Color.white.swiftUIColor) - } - .padding(.all, 12) - .background(GPleAsset.Color.gray1000.swiftUIColor) - .cornerRadius(8) + Text("인기 순위") + .foregroundStyle(GPleAsset.Color.white.swiftUIColor) } + .padding(.all, 12) + .background(GPleAsset.Color.gray1000.swiftUIColor) + .cornerRadius(8) + } - @ViewBuilder - func imageUploadButton() -> some View { - HStack { - GPleAsset.Assets.download.swiftUIImage + @ViewBuilder + func imageUploadButton() -> some View { + HStack { + GPleAsset.Assets.download.swiftUIImage - Text("사진 업로드") - .foregroundStyle(GPleAsset.Color.white.swiftUIColor) - } - .padding(.all, 12) - .background(GPleAsset.Color.gray1000.swiftUIColor) - .cornerRadius(8) + Text("사진 업로드") + .foregroundStyle(GPleAsset.Color.white.swiftUIColor) } + .padding(.all, 12) + .background(GPleAsset.Color.gray1000.swiftUIColor) + .cornerRadius(8) + } - @ViewBuilder - func postList( - name: String, - grade: Int, - title: String, - place: String, - tag: [String], - date: String, - imageURL: [String] - ) -> some View { - VStack(alignment: .leading, spacing: 0) { - HStack(spacing: 4) { - GPleAsset.Assets.profile.swiftUIImage - - Text(name) - .foregroundStyle(GPleAsset.Color.white.swiftUIColor) - .font(GPleFontFamily.Pretendard.semiBold.swiftUIFont(size: 16)) - .padding(.leading, 4) - - Text("•") - .foregroundStyle(GPleAsset.Color.gray800.swiftUIColor) - - HStack(spacing: 0) { - Text("\(grade)") + @ViewBuilder + func postList( + name: String, + grade: Int, + title: String, + place: String, + tag: [String], + date: String, + imageURL: [String] + ) -> some View { + VStack(alignment: .leading, spacing: 0) { + HStack(spacing: 4) { + GPleAsset.Assets.profile.swiftUIImage + + Text(name) + .foregroundStyle(GPleAsset.Color.white.swiftUIColor) + .font(GPleFontFamily.Pretendard.semiBold.swiftUIFont(size: 16)) + .padding(.leading, 4) - Text("학년") - } + Text("•") .foregroundStyle(GPleAsset.Color.gray800.swiftUIColor) - .font(GPleFontFamily.Pretendard.regular.swiftUIFont(size: 14)) + + HStack(spacing: 0) { + Text("\(grade)") + + Text("학년") } + .foregroundStyle(GPleAsset.Color.gray800.swiftUIColor) + .font(GPleFontFamily.Pretendard.regular.swiftUIFont(size: 14)) + } - Text(title) - .foregroundStyle(GPleAsset.Color.white.swiftUIColor) - .font(GPleFontFamily.Pretendard.semiBold.swiftUIFont(size: 18)) - .padding(.top, 16) - - Text(place) - .foregroundStyle(GPleAsset.Color.gray600.swiftUIColor) - .font(GPleFontFamily.Pretendard.regular.swiftUIFont(size: 14)) - .padding(.top, 4) - - ForEach(imageURL, id: \.self) { imageURL in - AsyncImage(url: URL(string: imageURL)) { phase in - switch phase { - case .empty: - ProgressView() - .frame(width: 318, height: 318) - case .success(let image): - image - .resizable() - .scaledToFit() - .frame(width: 318) - .cornerRadius(8) - case .failure: - Image(systemName: "exclamationmark.triangle.fill") - .foregroundColor(.red) - .frame(width: 318, height: 318) - @unknown default: - EmptyView() - } + Text(title) + .foregroundStyle(GPleAsset.Color.white.swiftUIColor) + .font(GPleFontFamily.Pretendard.semiBold.swiftUIFont(size: 18)) + .padding(.top, 16) + + Text(place) + .foregroundStyle(GPleAsset.Color.gray600.swiftUIColor) + .font(GPleFontFamily.Pretendard.regular.swiftUIFont(size: 14)) + .padding(.top, 4) + + ForEach(imageURL, id: \.self) { imageURL in + AsyncImage(url: URL(string: imageURL)) { phase in + switch phase { + case .empty: + ProgressView() + .frame(width: 318, height: 318) + case .success(let image): + image + .resizable() + .scaledToFit() + .frame(width: 318) + .cornerRadius(8) + case .failure: + Image(systemName: "exclamationmark.triangle.fill") + .foregroundColor(.red) + .frame(width: 318, height: 318) + @unknown default: + EmptyView() } } - .padding(.top, 16) + } + .padding(.top, 16) - HStack(spacing: 8) { - ForEach(tag, id: \.self) { tag in - HStack(spacing: 0) { - Text("@") + HStack(spacing: 8) { + ForEach(tag, id: \.self) { tag in + HStack(spacing: 0) { + Text("@") - Text(tag) - } + Text(tag) } } - .foregroundStyle(GPleAsset.Color.gray600.swiftUIColor) - .font(GPleFontFamily.Pretendard.regular.swiftUIFont(size: 14)) - .padding(.top, 12) + } + .foregroundStyle(GPleAsset.Color.gray600.swiftUIColor) + .font(GPleFontFamily.Pretendard.regular.swiftUIFont(size: 14)) + .padding(.top, 12) - Text(date) - .foregroundStyle(GPleAsset.Color.gray800.swiftUIColor) - .font(GPleFontFamily.Pretendard.regular.swiftUIFont(size: 14)) - .padding(.top, 4) + Text(date) + .foregroundStyle(GPleAsset.Color.gray800.swiftUIColor) + .font(GPleFontFamily.Pretendard.regular.swiftUIFont(size: 14)) + .padding(.top, 4) - } - .padding(.vertical, 20) - .padding(.horizontal, 16) - .background(GPleAsset.Color.gray1000.swiftUIColor) - .cornerRadius(12) } + .padding(.vertical, 20) + .padding(.horizontal, 16) + .background(GPleAsset.Color.gray1000.swiftUIColor) + .cornerRadius(12) } +} diff --git a/Projects/App/Sources/Feature/MyPageFeature/Sources/MyPageView.swift b/Projects/App/Sources/Feature/MyPageFeature/Sources/MyPageView.swift index 24a836a..49545fd 100644 --- a/Projects/App/Sources/Feature/MyPageFeature/Sources/MyPageView.swift +++ b/Projects/App/Sources/Feature/MyPageFeature/Sources/MyPageView.swift @@ -2,7 +2,6 @@ import SwiftUI import Domain struct MyPageView: View { - @StateObject var viewModel: MyPageViewModel @State private var topNavigationState = false @StateObject var postViewModel: PostViewModel @Environment(\.dismiss) private var dismiss @@ -120,20 +119,9 @@ struct MyPageView: View { ScrollView { LazyVGrid(columns: columns, spacing: 2) { - ForEach(postViewModel.myPostList.indices, id: \.self) { index in - let myPost = postViewModel.myPostList[index] - - let myPostEmojiArray = [ - myPost.emojiList.heartCount, - myPost.emojiList.congCount, - myPost.emojiList.thumbsCount, - myPost.emojiList.thinkCount, - myPost.emojiList.poopCount, - myPost.emojiList.chinaCount - ] + ForEach(postViewModel.myPostList, id: \.id) { myPost in NavigationLink(destination: DetailView( - viewModel: DetailViewModel(), postViewModel: PostViewModel(), postId: myPost.id, location: myPost.location, @@ -142,7 +130,14 @@ struct MyPageView: View { grade: myPost.author.grade, imageUrl: myPost.imageUrl, tagList: myPost.tagList.map { ($0.name, $0.id) }, - emojiList: myPostEmojiArray, + emojiList: [ + myPost.emojiList.heartCount, + myPost.emojiList.congCount, + myPost.emojiList.thumbsCount, + myPost.emojiList.thinkCount, + myPost.emojiList.poopCount, + myPost.emojiList.chinaCount + ], checkEmojiList: myPost.checkEmoji, createTime: myPost.createdTime )) { @@ -173,20 +168,9 @@ struct MyPageView: View { ScrollView { LazyVGrid(columns: columns1, spacing: 2) { - ForEach(postViewModel.myReactionPostList.indices, id: \.self) { index in - let rtPost = postViewModel.myReactionPostList[index] - - let emojiArray = [ - rtPost.emojiList.heartCount, - rtPost.emojiList.congCount, - rtPost.emojiList.thumbsCount, - rtPost.emojiList.thinkCount, - rtPost.emojiList.poopCount, - rtPost.emojiList.chinaCount - ] + ForEach(postViewModel.myReactionPostList, id: \.id) { rtPost in NavigationLink(destination: DetailView( - viewModel: DetailViewModel(), postViewModel: PostViewModel(), postId: rtPost.id, location: rtPost.location, @@ -195,7 +179,14 @@ struct MyPageView: View { grade: rtPost.author.grade, imageUrl: rtPost.imageUrl, tagList: rtPost.tagList.map { ($0.name, $0.id) }, - emojiList: emojiArray, + emojiList: [ + rtPost.emojiList.heartCount, + rtPost.emojiList.congCount, + rtPost.emojiList.thumbsCount, + rtPost.emojiList.thinkCount, + rtPost.emojiList.poopCount, + rtPost.emojiList.chinaCount + ], checkEmojiList: rtPost.checkEmoji, createTime: rtPost.createdTime )) { diff --git a/Projects/App/Sources/Feature/PostCreateFeature/Sources/PostCreateView.swift b/Projects/App/Sources/Feature/PostCreateFeature/Sources/PostCreateView.swift index 3090080..6fe571a 100644 --- a/Projects/App/Sources/Feature/PostCreateFeature/Sources/PostCreateView.swift +++ b/Projects/App/Sources/Feature/PostCreateFeature/Sources/PostCreateView.swift @@ -19,7 +19,6 @@ struct PostCreateView: View { @State private var tagUserYear: [Int] = [0, 0, 0, 0, 0] @State private var tagUserId: [Int?] = Array(repeating: nil, count: 5) @State private var toast: FancyToast? = nil - @State private var boolState: Bool = false @State private var buttonState: Bool = true var body: some View { @@ -346,7 +345,6 @@ struct PostCreateView: View { buttonState: isFormValid && buttonState, buttonOkColor: GPleAsset.Color.main.swiftUIColor ){ - print("클릭") buttonState = false toast = FancyToast(type: .info, title: "업로드 중...", message: "해당 게시물의 업로드가 진행중입니다. 잠시만 기다려주세요.") @@ -447,15 +445,13 @@ struct PostCreateView: View { userProfileImage: student.profileImage, userName: student.name, userYear: student.grade, - userId: student.id, - userProfileImageList: $tagUserImages[index], - userNameList: $tagUserName[index], - userYearList: $tagUserYear[index], - tagUserId: $tagUserId[index] + userId: student.id ) + .padding(.bottom, 20) } } + Spacer() } .frame(maxWidth: .infinity, maxHeight: .infinity) @@ -470,81 +466,84 @@ struct PostCreateView: View { .navigationBarBackButtonHidden(true) } - private var isFormValid: Bool { - return !titleTextField.isEmpty && - images[0] != nil - } -} - -@ViewBuilder -func searchUserList( - userProfileImage: String, - userName: String, - userYear: Int, - userId: Int, - userProfileImageList: Binding, - userNameList: Binding, - userYearList: Binding, - tagUserId: Binding -) -> some View { - Button { - userProfileImageList.wrappedValue = userProfileImage - userNameList.wrappedValue = userName - userYearList.wrappedValue = userYear - tagUserId.wrappedValue = userId - - print("추가: \(userId)") - print("추가: \(userName)") - print("추가: \(userYear)학년") - } label: { - HStack(spacing: 4) { - if let url = URL(string: userProfileImage) { - AsyncImage(url: url) { image in - image - .resizable() - .scaledToFit() - .frame(width: 32, height: 32) - .clipShape(Circle()) - } placeholder: { + @ViewBuilder + func searchUserList( + userProfileImage: String, + userName: String, + userYear: Int, + userId: Int + ) -> some View { + Button { + if let emptyIndex = tagUserName.firstIndex(of: "") { + tagUserImages[emptyIndex] = userProfileImage + tagUserName[emptyIndex] = userName + tagUserYear[emptyIndex] = userYear + tagUserId[emptyIndex] = userId + + print("추가된 유저 ID: \(userId)") + print("추가된 유저 이름: \(userName)") + print("추가된 유저 학년: \(userYear)") + } else { + tagUserImages[4] = userProfileImage + tagUserName[4] = userName + tagUserYear[4] = userYear + tagUserId[4] = userId + } + } label: { + HStack(spacing: 4) { + if let url = URL(string: userProfileImage) { + AsyncImage(url: url) { image in + image + .resizable() + .scaledToFit() + .frame(width: 32, height: 32) + .clipShape(Circle()) + } placeholder: { + GPleAsset.Assets.profile.swiftUIImage + .resizable() + .scaledToFit() + .frame(width: 32, height: 32) + } + .padding(.leading, 24) + } else { GPleAsset.Assets.profile.swiftUIImage .resizable() .scaledToFit() .frame(width: 32, height: 32) + .padding(.leading, 24) } - .padding(.leading, 24) - } else { - GPleAsset.Assets.profile.swiftUIImage - .resizable() - .scaledToFit() - .frame(width: 32, height: 32) - .padding(.leading, 24) - } - Text(userName) - .font(GPleFontFamily.Pretendard.semiBold.swiftUIFont(size: 16)) - .foregroundStyle(.white) - .padding(.leading, 4) + Text(userName) + .font(GPleFontFamily.Pretendard.semiBold.swiftUIFont(size: 16)) + .foregroundStyle(.white) + .padding(.leading, 4) - Text("· \(userYear)학년") - .font(GPleFontFamily.Pretendard.regular.swiftUIFont(size: 14)) - .foregroundStyle(GPleAsset.Color.gray800.swiftUIColor) + Text("· \(userYear)학년") + .font(GPleFontFamily.Pretendard.regular.swiftUIFont(size: 14)) + .foregroundStyle(GPleAsset.Color.gray800.swiftUIColor) - Spacer() + Spacer() - HStack(spacing: 8) { - GPleAsset.Assets.grayUserPlus.swiftUIImage + HStack(spacing: 8) { + GPleAsset.Assets.grayUserPlus.swiftUIImage - Text("추가하기") - .font(GPleFontFamily.Pretendard.regular.swiftUIFont(size: 14)) - .foregroundStyle(GPleAsset.Color.gray400.swiftUIColor) + Text("추가하기") + .font(GPleFontFamily.Pretendard.regular.swiftUIFont(size: 14)) + .foregroundStyle(GPleAsset.Color.gray400.swiftUIColor) + } + .padding(.horizontal, 12) + .padding(.vertical, 8) + .background( + RoundedRectangle(cornerRadius: 8) + .foregroundStyle(GPleAsset.Color.gray1000.swiftUIColor) + ) + .padding(.trailing, 20) } - .padding(.horizontal, 12) - .padding(.vertical, 8) - .background( - RoundedRectangle(cornerRadius: 8) - .foregroundStyle(GPleAsset.Color.gray1000.swiftUIColor) - ) - .padding(.trailing, 20) } } + + private var isFormValid: Bool { + return !titleTextField.isEmpty && + images[0] != nil + } } diff --git a/Projects/App/Sources/Feature/PostCreateFeature/Sources/PostViewModel.swift b/Projects/App/Sources/Feature/PostCreateFeature/Sources/PostViewModel.swift index 288c97a..d1d9d53 100644 --- a/Projects/App/Sources/Feature/PostCreateFeature/Sources/PostViewModel.swift +++ b/Projects/App/Sources/Feature/PostCreateFeature/Sources/PostViewModel.swift @@ -163,20 +163,29 @@ public final class PostViewModel: ObservableObject { switch result { case let .success(response): do { - print("성공ㅣ유저 리스트 불러오기") + // 응답 데이터를 String으로 변환하여 출력 + if let responseString = String(data: response.data, encoding: .utf8) { + print("Response Data as String: \(responseString)") + } + + // JSON 디코딩 + print("성공: 유저 리스트 불러오기") self.allUserList = try JSONDecoder().decode([UserListResponse].self, from: response.data) completion(true) } catch { - print("Failed to decode JSON response") + // 디코딩 실패 시 에러 출력 + print("Failed to decode JSON response: \(error)") completion(false) } case let .failure(err): + // 네트워크 요청 실패 시 에러 출력 print("Network request failed: \(err)") completion(false) } } } + public func popularityUserList(completion: @escaping (Bool) -> Void) { authProvider.request(.popularityUserList(authorization: accessToken)) { result in switch result { diff --git a/Projects/App/Sources/Feature/RankFeature/Sources/RankView.swift b/Projects/App/Sources/Feature/RankFeature/Sources/RankView.swift index f633594..bad36f2 100644 --- a/Projects/App/Sources/Feature/RankFeature/Sources/RankView.swift +++ b/Projects/App/Sources/Feature/RankFeature/Sources/RankView.swift @@ -128,7 +128,6 @@ struct RankView: View { .padding(.top, 15) DetailView( - viewModel: DetailViewModel(), postViewModel: PostViewModel(), postId: popularityPost.id, title: popularityPost.title, @@ -169,5 +168,6 @@ struct RankView: View { } } } + .navigationBarBackButtonHidden(true) } } diff --git a/Projects/App/Sources/Feature/SplashFeature/Sources/SplashView.swift b/Projects/App/Sources/Feature/SplashFeature/Sources/SplashView.swift new file mode 100644 index 0000000..44e518b --- /dev/null +++ b/Projects/App/Sources/Feature/SplashFeature/Sources/SplashView.swift @@ -0,0 +1,35 @@ +import SwiftUI + +struct SplashView: View { + @State private var isActive = false + + var body: some View { + ZStack { + if isActive { + MainView(viewModel: MainViewModel(), postViewModel: PostViewModel()) + .transition(.opacity) + } else { + ZStack { + GPleAsset.Color.back.swiftUIColor + .ignoresSafeArea() + + VStack { + GPleAsset.Assets.gpleBigLogo.swiftUIImage + .resizable() + .frame(width: 160, height: 70) + .padding(.bottom, 40) + } + } + .transition(.opacity) + } + } + .animation(.easeInOut(duration: 1), value: isActive) + .onAppear { + DispatchQueue.main.asyncAfter(deadline: .now() + 1.5) { + withAnimation { + isActive = true + } + } + } + } +} diff --git a/README.md b/README.md index 86ac0c1..250d595 100644 --- a/README.md +++ b/README.md @@ -1,86 +1,21 @@ -# Template 개요 -## Template 사용법 -- 해당 레포지토리에서 `Use this template` 를 사용하여 Github에 Repo를 만들어 시작 -- 해당 레포지토리를 Fork 혹은 Download하여 시작 - -## 레이어 -Features - Services - Core - UserInterface - Shared -5개의 레이어를 가집니다. - -- Feature - - 사용자의 액션을 처리하거나 데이터를 보여주는, 사용자와 직접 맞닿는 레이어 - - ex) AuthFeature, ProfileFeature -- Domain - - 도메인 로직이 진행되는 레이어 - - ex) AuthDomain, ProfileDomain -- Core - - 앱의 비즈니스를 포함하지 않고 순수 기능성 모듈이 위치한 레이어 - - ex) NetworkingModule, DatabaseModule -- UserInterface - - 공용 View, 디자인 시스템, 리소스 등 UI 요소 모듈이 위치한 레이어 - - ex) DesignSystem, LocalizableManager -- Shared - - 로깅, extension 등 모든 레이어에서 공용으로 재사용될 모듈이 위치한 레이어 - - ex) UtilityModule, LoggingModule - -을 생각하여 레이어를 분리하였습니다. - -## Micro Feature -각 모듈은 Micro Feature 구조를 기반으로 설계됩니다. -확장 가능하고 커지는 프로젝트를 기능별로 수평 확장이 가능하도록 Micro Service에서 영감을 얻은 아키텍쳐입니다. - - - -##### https://docs.tuist.io/building-at-scale/microfeatures/#product - -## 프로젝트 세팅 -프로젝트 루트에서 `make init` 를 실행하여, 프로젝트 이름과 organization 이름을 입력하여 기본 설정을 할 수 있습니다. - -프로젝트 루트에서 `make signing`를 실행하면 프로젝트 Team Signing을 할 수 있습니다. - -## 모듈 생성 -프로젝트 루트에서 `make module`를 실행하면 모듈 레이어, 이름, Micro Feature 종류를 선택하여 새 모듈을 생성합니다. - -## Makefile -프로젝트 루트에서 실행할 수 있는 명령어입니다. -- make init : `프로젝트 이름과 organization을 입력하여 프로젝트 기본 세팅` - - swift Scripts/InitEnvironment.swift - -- make signing : `프로젝트 Team Signing` - - swift Scripts/CodeSigning.swift - -- make generate : `외부 디펜던시 fetch 및 프로젝트 generate` - - tuist fetch - - tuist generate - -- make module : `모듈 생성` - - swift Scripts/GenerateModule.swift - -- make dependency : `디펜던시 추가` - - swift Scripts/NewDependency.swift - -- make ci_generate : `디펜던시 fetch 및 CI용 프로젝트 generate (SwiftLint X)` - - tuist fetch - - TUIST_ENV=CI tuist generate - -- make cd_generate : `디펜던시 fetch 및 CI용 프로젝트 generate (SwiftLint X)` - - tuist fetch - - TUIST_ENV=CD tuist generate - -- make clean : `전체 xcodeproj, xcworkspace 파일 삭제` - - rm -rf **/*.xcodeproj - - rm -rf *.xcworkspace - -- make reset : `tuist clean 후, 전체 xcodeproj, xcworkspace 파일 삭제` - - tuist clean - - rm -rf **/*.xcodeproj - - rm -rf *.xcworkspace - -## Scaffold -```sh -tuist Scaffold(Demo/Interface/Sources/Testing/Tests/UITests) - --layer (Features/Services/Core/Shared/UserInterface 레이어 이름) - --name (모듈 이름) -``` - -으로 Project 모듈의 Target 모듈을 직접 생성 가능합니다. +![ppt](https://github.com/user-attachments/assets/fe8c2b3e-e0ee-49bd-9ac7-b2f8e8d30515) +ㅤ +# 🤔ㅣGPle? +"**GPle이란?** GSM Place의 줄임말로, GSM 아이디어페스티벌을 위해 7기 학생들이 한 팀이 되어 만든 프로젝트입니다. +이 앱은 GSM의 다양한 장소에서 촬영한 사진을 **손쉽게 공유**하고, 위치별로 모인 사진들을 통해 +**서로의 추억**을 되새기며 **즐거운 순간**들을 함께 나누는 경험을 제공합니다." + +ㅤ +### GPle 화면 +|온보딩|메인|자세히보기|랭킹|마이페이지 +|:---:|:---:|:---:|:---:|:---:| +||||| + +ㅤ +# 🛠️ㅣTechStack +- SwiftUI +- Tuist +- Mvvm +- Github Action +- Moya +- GoogleSignIn