diff --git a/iOS/RollTheDice/RollTheDice.xcodeproj/project.pbxproj b/iOS/RollTheDice/RollTheDice.xcodeproj/project.pbxproj index 9d4f2cfb..04a94122 100644 --- a/iOS/RollTheDice/RollTheDice.xcodeproj/project.pbxproj +++ b/iOS/RollTheDice/RollTheDice.xcodeproj/project.pbxproj @@ -69,6 +69,8 @@ 6CC4DDCE2B5574690080E7E8 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 6CC4DDCD2B5574690080E7E8 /* Preview Assets.xcassets */; }; 6CC6737A2C217C4B009FB30E /* ScoopAPILogin.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6CC673792C217C4B009FB30E /* ScoopAPILogin.swift */; }; 6CC6737C2C21BA5B009FB30E /* Auth.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6CC6737B2C21BA5B009FB30E /* Auth.swift */; }; + 6CC6737E2C233443009FB30E /* StatisticsService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6CC6737D2C233443009FB30E /* StatisticsService.swift */; }; + 6CC673802C233A65009FB30E /* ScoopAPIStatistics.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6CC6737F2C233A65009FB30E /* ScoopAPIStatistics.swift */; }; 6CDB29C92BA97C550081037B /* Pretendard-Black.otf in Resources */ = {isa = PBXBuildFile; fileRef = 6CDB29C02BA97C550081037B /* Pretendard-Black.otf */; }; 6CDB29CA2BA97C550081037B /* Pretendard-ExtraBold.otf in Resources */ = {isa = PBXBuildFile; fileRef = 6CDB29C12BA97C550081037B /* Pretendard-ExtraBold.otf */; }; 6CDB29CB2BA97C550081037B /* Pretendard-Regular.otf in Resources */ = {isa = PBXBuildFile; fileRef = 6CDB29C22BA97C550081037B /* Pretendard-Regular.otf */; }; @@ -162,6 +164,8 @@ 6CC4DDCD2B5574690080E7E8 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; }; 6CC673792C217C4B009FB30E /* ScoopAPILogin.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScoopAPILogin.swift; sourceTree = ""; }; 6CC6737B2C21BA5B009FB30E /* Auth.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Auth.swift; sourceTree = ""; }; + 6CC6737D2C233443009FB30E /* StatisticsService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatisticsService.swift; sourceTree = ""; }; + 6CC6737F2C233A65009FB30E /* ScoopAPIStatistics.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScoopAPIStatistics.swift; sourceTree = ""; }; 6CDB29BF2BA9735C0081037B /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 6CDB29C02BA97C550081037B /* Pretendard-Black.otf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "Pretendard-Black.otf"; sourceTree = ""; }; 6CDB29C12BA97C550081037B /* Pretendard-ExtraBold.otf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "Pretendard-ExtraBold.otf"; sourceTree = ""; }; @@ -635,6 +639,7 @@ 6CF130B12BAB74BA00A437B6 /* NewsService.swift */, 6C5B0C8B2C1C32C000A0D5F4 /* BookmarksService.swift */, 357699432C09C7B900AD2DA4 /* LoginService.swift */, + 6CC6737D2C233443009FB30E /* StatisticsService.swift */, ); path = Service; sourceTree = ""; @@ -664,6 +669,7 @@ 6CF130C62BAB7B9800A437B6 /* ScoopAPINews.swift */, 6C5B0C892C1C308A00A0D5F4 /* ScoopAPIBookmarks.swift */, 6CC673792C217C4B009FB30E /* ScoopAPILogin.swift */, + 6CC6737F2C233A65009FB30E /* ScoopAPIStatistics.swift */, ); path = ScoopAPI; sourceTree = ""; @@ -820,6 +826,7 @@ 6CDB29FB2BAA07B10081037B /* GPTChatViewModel.swift in Sources */, 6C32379F2B7C376D00B699AB /* Bookmarks.swift in Sources */, 6C454A7E2B9DAA3F006FD9D0 /* SignUpFinishView.swift in Sources */, + 6CC673802C233A65009FB30E /* ScoopAPIStatistics.swift in Sources */, 6C7704A12B722CEB001B17CB /* ProfileView.swift in Sources */, 6C4F7BAB2BDE50C600ED01DA /* DailyReportModel.swift in Sources */, 6C3237B72B7C434600B699AB /* ChatType.swift in Sources */, @@ -832,6 +839,7 @@ 6C41B8DA2BE104A800274FA4 /* RecentNewsCardView.swift in Sources */, 3509091A2C1C1248007D76A1 /* TokenManager.swift in Sources */, 6CF130C72BAB7B9800A437B6 /* ScoopAPINews.swift in Sources */, + 6CC6737E2C233443009FB30E /* StatisticsService.swift in Sources */, 6C7651402BF37F3400196536 /* Log.swift in Sources */, 6CE1031A2BD57A2500498AA4 /* DebateSummaryView.swift in Sources */, 6CE1030C2BD56A4000498AA4 /* TypeReportView.swift in Sources */, diff --git a/iOS/RollTheDice/RollTheDice/RollTheDiceApp.swift b/iOS/RollTheDice/RollTheDice/RollTheDiceApp.swift index 3ea1b27f..5009d613 100644 --- a/iOS/RollTheDice/RollTheDice/RollTheDiceApp.swift +++ b/iOS/RollTheDice/RollTheDice/RollTheDiceApp.swift @@ -40,7 +40,7 @@ struct RollTheDiceApp: App { .environmentObject(signUpViewModel) case .completedSignUp: NavigationStack(path: $pathModel.paths) { - MainTabView(newsListViewModel: newsListViewModel) + MainTabView(newsListViewModel: newsListViewModel, bookmarksListViewModel: bookmarkListViewModel) .navigationDestination(for: PathType.self, destination: { pathType in // 각 뷰마다 .navigationBarBackButtonHidden() 설정하기! diff --git a/iOS/RollTheDice/RollTheDice/Source/Domain/ScoopAPI/ScoopAPIStatistics.swift b/iOS/RollTheDice/RollTheDice/Source/Domain/ScoopAPI/ScoopAPIStatistics.swift new file mode 100644 index 00000000..22424bdf --- /dev/null +++ b/iOS/RollTheDice/RollTheDice/Source/Domain/ScoopAPI/ScoopAPIStatistics.swift @@ -0,0 +1,15 @@ +// +// ScoopAPIStatistics.swift +// RollTheDice +// +// Created by Subeen on 6/20/24. +// + +import Foundation + +public enum ScoopAPIStatistics { +// public static let statistics = String("/statistics") + public static let statisticsPerDates = String("/statistics/per-dates") + public static let statisticsCategories = String("/statistics/categories") + +} diff --git a/iOS/RollTheDice/RollTheDice/Source/Domain/Service/BookmarksService.swift b/iOS/RollTheDice/RollTheDice/Source/Domain/Service/BookmarksService.swift index 926408bc..1d24ede8 100644 --- a/iOS/RollTheDice/RollTheDice/Source/Domain/Service/BookmarksService.swift +++ b/iOS/RollTheDice/RollTheDice/Source/Domain/Service/BookmarksService.swift @@ -12,7 +12,7 @@ enum BookmarksService { case bookmarksIsChecked(newsId: Int, accessToken: String) ///뉴스 북마크 여부 조회 case saveBookmarks(newsId: Int, accessToken: String) ///북마크 저장 case deleteBookmarks(newsId: Int, accessToken: String) ///북마크 삭제 - case allBookmarks(page: Int, size: Int, accessToken: String) ///북마크 전체 조회/ + case allBookmarks(page: Int, size: Int, accessToken: String) ///북마크 전체 조회 } extension BookmarksService: BaseTargetType { @@ -49,12 +49,14 @@ extension BookmarksService: BaseTargetType { var task: Moya.Task { switch self { - case .bookmarksIsChecked(let newsId, _), - .saveBookmarks(let newsId, _), - .deleteBookmarks(let newsId, _): + case .bookmarksIsChecked(_, _), + .deleteBookmarks(_, _): let parameters : [String : Any] = [:] return .requestParameters(parameters: parameters, encoding: URLEncoding.default) + case .saveBookmarks(_, _): + return .requestPlain + case .allBookmarks(let page, let size, _): let parameters : [String : Any] = [ "page" : page, diff --git a/iOS/RollTheDice/RollTheDice/Source/Domain/Service/StatisticsService.swift b/iOS/RollTheDice/RollTheDice/Source/Domain/Service/StatisticsService.swift new file mode 100644 index 00000000..9ff4993b --- /dev/null +++ b/iOS/RollTheDice/RollTheDice/Source/Domain/Service/StatisticsService.swift @@ -0,0 +1,63 @@ +// +// StatisticsService.swift +// RollTheDice +// +// Created by Subeen on 6/20/24. +// + +import Foundation +import Moya + +enum StatisticsService { + case recentSevenPerDate(accessToken: String) /// 최근 일주일 날짜별 뉴스 조회수 조회 + case statisticsCategory(accessToken: String) /// 카테고리별 조회수 조회 +} + +extension StatisticsService: BaseTargetType { + var baseURL: URL { + return URL(string: ScoopAPI.baseURL)! + } + + var path: String { + switch self { + case .recentSevenPerDate(let accessToken): + return ScoopAPIStatistics.statisticsPerDates + + case .statisticsCategory(let accessToken): + return ScoopAPIStatistics.statisticsCategories + } + } + + var method: Moya.Method { + switch self { + case .recentSevenPerDate, + .statisticsCategory: + return .get + + } + } + + var task: Moya.Task { + switch self { + case .recentSevenPerDate(_), + .statisticsCategory(_): + + let parameters : [String : Any] = [ : ] + return .requestPlain + } + } + + var headers: [String : String]? { + let accessToken: String + + switch self { + case .recentSevenPerDate(let accessTokenValue), + .statisticsCategory(let accessTokenValue): + accessToken = accessTokenValue + return [ + "Authorization": "Bearer \(accessToken)", + "X-Content-Type_Options" : "nosniff" + ] + } + } +} diff --git a/iOS/RollTheDice/RollTheDice/Source/Model/Report/NewsType.swift b/iOS/RollTheDice/RollTheDice/Source/Model/Report/NewsType.swift index 99deabbe..ac4c22df 100644 --- a/iOS/RollTheDice/RollTheDice/Source/Model/Report/NewsType.swift +++ b/iOS/RollTheDice/RollTheDice/Source/Model/Report/NewsType.swift @@ -8,13 +8,13 @@ import Foundation import SwiftUI -enum NewsType { - case politics // 정치 - case economy // 경제 - case society // 사회 - case living // 생활/문화 - case world // 세계 - case science // IT/과학 +enum NewsType: String, Codable { + case politics = "정치" // 정치 + case economy = "경제" // 경제 + case society = "사회" // 사회 + case living = "생활/문화" // 생활/문화 + case world = "세계" // 세계 + case science = "IT/과학" // IT/과학 var desciption: String { switch self { diff --git a/iOS/RollTheDice/RollTheDice/Source/View/Bookmark/BookmarkCard/BookmarkView.swift b/iOS/RollTheDice/RollTheDice/Source/View/Bookmark/BookmarkCard/BookmarkView.swift index ea1086b4..f6ddfdd7 100644 --- a/iOS/RollTheDice/RollTheDice/Source/View/Bookmark/BookmarkCard/BookmarkView.swift +++ b/iOS/RollTheDice/RollTheDice/Source/View/Bookmark/BookmarkCard/BookmarkView.swift @@ -7,11 +7,6 @@ import SwiftUI -//let id = UUID().uuidString -//let newsId: Int -//let title, thumbnailURL, postDate: String -//let isBookmarked: Bool - struct BookmarkView: View { @State var bookmark: Bookmarks var body: some View { @@ -73,9 +68,3 @@ struct BookmarkView: View { } } } - -//#Preview { -// BookmarkView(bookmark: .init(newsId: 0, title: "NHN, 작년 영업익 555억원...전년비 42% ↑", thumbnailURL: <#T##String#>, postDate: <#T##String#>, isBookmarked: <#T##Bool#>)) -// -// BookmarkView(bookmark: .init(title: "NHN, 작년 영업익 555억원...전년비 42% ↑", date: "2023년2월13일", image: "https://cdnimage.dailian.co.kr/news/202402/news_1707866329_1327972_m_1.png", content: "2NHN은 연결기준 지난해 영업이익이 555억원으로 전년 대비 42.2% 증가했다고 14일 밝혔다.같은 기간 매출은 7.3% 증가한 2조2696억원으로 연간 최대치를 기록했다. 작년 4분기 매출은 5983억원으로 전년 동기 대비 6.7% 올랐다. 반면 영업손실은 78억원으로 적자전환했다. 커머스 부문의 장기 미회수채권 대손상각비 인식과 기술 부문의 기 인식 매출 차감 등 일회성 요인이 영향을 미쳤다.", isBookmarked: false)) -//} diff --git a/iOS/RollTheDice/RollTheDice/Source/View/Bookmark/BookmarkCard/BookmarksListViewModel.swift b/iOS/RollTheDice/RollTheDice/Source/View/Bookmark/BookmarkCard/BookmarksListViewModel.swift index fc069789..f31a3395 100644 --- a/iOS/RollTheDice/RollTheDice/Source/View/Bookmark/BookmarkCard/BookmarksListViewModel.swift +++ b/iOS/RollTheDice/RollTheDice/Source/View/Bookmark/BookmarkCard/BookmarksListViewModel.swift @@ -23,8 +23,13 @@ import SwiftUI } extension BookmarksListViewModel { + + /// 북마크 전체 조회 public func getAllBookmarksData(page: Int, size: Int) { - let accessToken = "5Gu45OmOoZnvANl-qfZv-xHv_2eSweVeAAAAAQopyNkAAAGQFbGnJSn2EFsnJsRZ" + guard let accessToken = TokenManager.shared.accessToken else { + print("Access token 사용 불가능...") + return + } if let cancellable = bookmarksCancellable { cancellable.cancel() @@ -53,26 +58,126 @@ extension BookmarksListViewModel { print(response) }) } -} + + /// 뉴스 북마크 여부 조회 + public func isBookmark(newsId: Int) -> Bool { + + var isBookmarked: Bool = false + + guard let accessToken = TokenManager.shared.accessToken else { + print("Access token 사용 불가능...") + return isBookmarked + } + + if let cancellable = bookmarksCancellable { + cancellable.cancel() + } + + bookmarksCancellable = provider.requestWithProgressPublisher( + .saveBookmarks( + newsId: newsId, + accessToken: accessToken + ) + ) + .compactMap { $0.response?.data } + .receive(on: DispatchQueue.main) + .decode(type: Bookmarks.self, decoder: JSONDecoder()) + .sink(receiveCompletion: { result in + switch result { + case .finished: + print("bookmark 여부 확인 성공") + case .failure(let error): + Log.network("network error bookmark 여부 확인 실패", error.localizedDescription) + } + }, receiveValue: { [weak self] response in + print(response) + + if response.isBookmarked ?? false { + isBookmarked = true + } else { + isBookmarked = false + } + }) + + return isBookmarked + } + + /// 북마크 저장 + public func saveBookmark(newsId: Int) { + print("saveBookmark \(newsId)") + + guard let accessToken = TokenManager.shared.accessToken else { + print("Access token 사용 불가능...") + return + } + + if let cancellable = bookmarksCancellable { + cancellable.cancel() + } + + bookmarksCancellable = provider.requestWithProgressPublisher( + .saveBookmarks( + newsId: newsId, + accessToken: accessToken + ) + ) +// .compactMap { $0.response?.data } + .receive(on: DispatchQueue.main) +// .decode(type: Bookmarks.self, decoder: JSONDecoder()) + .sink(receiveCompletion: { result in + switch result { + case .finished: + print("bookmarks 저장") + case .failure(let error): + Log.network("network error bookmark 여부 확인 실패", error.localizedDescription) + } + }, receiveValue: { [weak self] response in + print(response) + if response.response?.statusCode == 201 { + print("bookmarks 저장 성공") + } else { + print("bookmarks 저장 실패") + } + }) + + } + + /// 북마크 삭제 + public func deleteBookmark(newsId: Int) { + print("saveBookmark \(newsId)") + + guard let accessToken = TokenManager.shared.accessToken else { + print("Access token 사용 불가능...") + return + } + + if let cancellable = bookmarksCancellable { + cancellable.cancel() + } + + bookmarksCancellable = provider.requestWithProgressPublisher( + .deleteBookmarks( + newsId: newsId, + accessToken: accessToken + ) + ) + .receive(on: DispatchQueue.main) + .sink(receiveCompletion: { result in + switch result { + case .finished: + print("bookmarks 삭제") + case .failure(let error): + Log.network("network error bookmark 여부 확인 실패", error.localizedDescription) + } + }, receiveValue: { [weak self] response in + print(response) + if response.response?.statusCode == 204 { + print("bookmarks 삭제 성공") + } else { + print("bookmarks 삭제 실패") + } + }) -//public class BookmarksListViewModel: ObservableObject { -// @Published var bookmarkList: [Bookmarks] -// -// init( -// bookmarkList: [Bookmarks] = [ -// .init(title: "2024년 ‘소셜 미디어 다이어트’를 위해 바꿔볼 것", date: "2023년12월3일", image: "exampleNews", content: "2024년으로 접어든지 한 달이 넘었다. 하지만 올 해가 어떻게 흘러갈지 예측하기는 쉽지 않다. 한 가지 확실한 것은 정치적으로 매우 중요한 해라는 점이다. 미국과 러시아, 우크라이나, 방글라데시, 인도, 대만, 한국, 남아프리카공화국, 유럽의회, 영국에서 선거가 치러질 예정이다.", isBookmarked: false), -// .init(title: "앤스로픽 최신 AI 모델 '클로드3' 출시", date: "2023년3월13일", image: "https://imgnews.pstatic.net/image/014/2024/03/05/0005151141_001_20240305101610000.jpg?type=w647", content: "오픈AI의 대항마 앤스로픽이 생성형 인공지능(AI) 최신 모델 '클로드'(Claude)3를 선보이면서 생성형 AI 주도권을 잡기 위한 경쟁이 다시 뜨거워지고 있다. 앤스로픽은 지난 한 해 동안 구글과 세일즈포스, 아마존 등에서 총 73억 달러(약 9조7309억 원)를 투자받고 '클로드3'를 내놨는데 오픈AI의 챗GPT-4를 능가한다고 도발했다.", isBookmarked: true), -// .init(title: "SK C&C, 외부 전문가 대거 영입… “신성장 동력 강화”", date: "2023년2월13일", image: "https://imgnews.pstatic.net/image/366/2024/03/05/0000975131_001_20240305093504301.jpg?type=w647", content: "SK C&C는 국내외 신성장 동력 강화를 위해 인공지능(AI)·클라우드·디지털 팩토리·ESG(환경·사회·지배구조) 등 4대 성장 사업과 디지털 컨설팅 중심으로 외부 전문가를 대거 영입해 전진 배치했다고 5일 밝혔다.", isBookmarked: false), -// .init(title: "NHN, 작년 영업익 555억원...전년비 42% ↑", date: "2023년2월13일", image: "https://cdnimage.dailian.co.kr/news/202402/news_1707866329_1327972_m_1.png", content: "2NHN은 연결기준 지난해 영업이익이 555억원으로 전년 대비 42.2% 증가했다고 14일 밝혔다.같은 기간 매출은 7.3% 증가한 2조2696억원으로 연간 최대치를 기록했다. 작년 4분기 매출은 5983억원으로 전년 동기 대비 6.7% 올랐다. 반면 영업손실은 78억원으로 적자전환했다. 커머스 부문의 장기 미회수채권 대손상각비 인식과 기술 부문의 기 인식 매출 차감 등 일회성 요인이 영향을 미쳤다.", isBookmarked: false), -// -// .init(title: "이종호 과기장관 5년 뒤 구축될 바이오파운드리에 산학연 역량 모아야", date: "2023년3월13일", image: "https://imgnews.pstatic.net/image/003/2024/03/05/NISI20231030_0001398722_web_20231030141808_20240305103108748.jpg?type=w647", content: "정부가 내년부터 합성생물학의 필수 인프라인 '국가 바이오파운드리' 구축에 본격 나서는 가운데 이종호 과학기술정보통신부 장관이 한국생명공학연구원에서 운영 중인 사전연구용 시설을 찾아 구체적인 인프라 구축 계획 등을 점검했다.이 장관은 5일 합성생물학 육성을 위해 바이오파운드리 연구현장을 방문하고, 산·학·연 전문가들을 만났다. 이번 방문은 지난해 미국, 영국과의 정상회담 시 논의됐던 첨단바이오 협력을 위한 후속조치의 일환으로 추진됐다.", isBookmarked: false), -// .init(title: "SW산업 육성에 7308억 예산투자, SaaS 등 육성", date: "2023년3월13일", image: "https://imgnews.pstatic.net/image/008/2024/03/05/0005007507_001_20240305135301539.jpg?type=w647", content: "SW(소프트웨어) 산업의 SaaS(서비스형 소프트웨어) 전환 등 SW산업 육성을 위해 정부 예산 7308억원이 투자된다. 강도현 과학기술정보통신부 제2차관은 5일 오전 서울 을지로 더존비즈온 을지사옥에서 열린 'AI(인공지능) 일상화, SW도 이제 서비스형 SW'라는 주제로 열린 현장 간담회에서 정부도 대한민국 SW산업의 미래가 SaaS에 있다고 생각하고 올해 'SaaS 혁신펀드'를 새롭게 조성하는 등 SaaS 육성 및 기존 SW 기업의 SaaS 전환을 위해 다양한 지원을 추진 중이라며 이같이 밝혔다.", isBookmarked: false), -// .init(title: "파수, 기업용 AI ‘엔터프라이즈 LLM’ 출시", date: "2023년3월13일", image: "https://imgnews.pstatic.net/image/018/2024/03/05/0005685566_001_20240305103201036.jpg?type=w647", content: "2NHN은 연결기준 지난해 영업이익이 555억원으로 전년 대비 42.2% 증가했다고 14일 밝혔다.같은 기간 매출은 7.3% 증가한 2조2696억원으로 연간 최대치를 기록했다. 작년 4분기 매출은 5983억원으로 전년 동기 대비 6.7% 올랐다. 반면 영업손실은 78억원으로 적자전환했다. 커머스 부문의 장기 미회수채권 대손상각비 인식과 기술 부문의 기 인식 매출 차감 등 일회성 요인이 영향을 미쳤다.", isBookmarked: false), -// .init(title: "NHN, 작년 영업익 555억원...전년비 42% ↑", date: "2023년2월13일", image: "https://cdnimage.dailian.co.kr/news/202402/news_1707866329_1327972_m_1.png", content: "2NHN은 연결기준 지난해 영업이익이 555억원으로 전년 대비 42.2% 증가했다고 14일 밝혔다.같은 기간 매출은 7.3% 증가한 2조2696억원으로 연간 최대치를 기록했다. 작년 4분기 매출은 5983억원으로 전년 동기 대비 6.7% 올랐다. 반면 영업손실은 78억원으로 적자전환했다. 커머스 부문의 장기 미회수채권 대손상각비 인식과 기술 부문의 기 인식 매출 차감 등 일회성 요인이 영향을 미쳤다.", isBookmarked: false), -// .init(title: "NHN, 작년 영업익 555억원...전년비 42% ↑", date: "2023년2월13일", image: "https://cdnimage.dailian.co.kr/news/202402/news_1707866329_1327972_m_1.png", content: "2NHN은 연결기준 지난해 영업이익이 555억원으로 전년 대비 42.2% 증가했다고 14일 밝혔다.같은 기간 매출은 7.3% 증가한 2조2696억원으로 연간 최대치를 기록했다. 작년 4분기 매출은 5983억원으로 전년 동기 대비 6.7% 올랐다. 반면 영업손실은 78억원으로 적자전환했다. 커머스 부문의 장기 미회수채권 대손상각비 인식과 기술 부문의 기 인식 매출 차감 등 일회성 요인이 영향을 미쳤다.", isBookmarked: false), -// .init(title: "NHN, 작년 영업익 555억원...전년비 42% ↑", date: "2023년2월13일", image: "https://cdnimage.dailian.co.kr/news/202402/news_1707866329_1327972_m_1.png", content: "2NHN은 연결기준 지난해 영업이익이 555억원으로 전년 대비 42.2% 증가했다고 14일 밝혔다.같은 기간 매출은 7.3% 증가한 2조2696억원으로 연간 최대치를 기록했다. 작년 4분기 매출은 5983억원으로 전년 동기 대비 6.7% 올랐다. 반면 영업손실은 78억원으로 적자전환했다. 커머스 부문의 장기 미회수채권 대손상각비 인식과 기술 부문의 기 인식 매출 차감 등 일회성 요인이 영향을 미쳤다.", isBookmarked: false), -// ] -// ) { -// self.bookmarkList = bookmarkList -// } -//} + } + +} diff --git a/iOS/RollTheDice/RollTheDice/Source/View/Bookmark/BookmarkListView.swift b/iOS/RollTheDice/RollTheDice/Source/View/Bookmark/BookmarkListView.swift index ae4c8175..d7bb4c80 100644 --- a/iOS/RollTheDice/RollTheDice/Source/View/Bookmark/BookmarkListView.swift +++ b/iOS/RollTheDice/RollTheDice/Source/View/Bookmark/BookmarkListView.swift @@ -67,30 +67,6 @@ struct BookmarkListView: View { } } } - -// private struct BookmarkListContentView: View { -// @StateObject var bookmarkListViewModel: BookmarkListViewModel -// var columns: [GridItem] = [ GridItem(), GridItem()] -// -// fileprivate var body: some View { -// ScrollViewReader { value in -// ScrollView(.horizontal, showsIndicators: false) { -// LazyHGrid(rows: columns, spacing: 10) { -// ForEach(bookmarkListViewModel.bookmarkList, id: \.self) { bookmark in -// BookmarkView(bookmark: bookmark) -//// .onTapGesture { -//// withAnimation { -//// selectedIndex = index -//// value.scrollTo(index) -//// } -//// } -// } -// } -// .padding(.vertical, 90) -// } -// } -// } -// } } #Preview { diff --git a/iOS/RollTheDice/RollTheDice/Source/View/MainTab/MainTabView.swift b/iOS/RollTheDice/RollTheDice/Source/View/MainTab/MainTabView.swift index 242bc53b..491633c9 100644 --- a/iOS/RollTheDice/RollTheDice/Source/View/MainTab/MainTabView.swift +++ b/iOS/RollTheDice/RollTheDice/Source/View/MainTab/MainTabView.swift @@ -11,6 +11,7 @@ struct MainTabView: View { @EnvironmentObject private var pathModel: PathModel var newsListViewModel: NewsListViewModel + var bookmarksListViewModel: BookmarksListViewModel @EnvironmentObject var authViewModel: AuthenticationViewModel @@ -37,7 +38,7 @@ struct MainTabView: View { } .tag(0) - NewsListView(newsListViewModel: newsListViewModel) + NewsListView(newsListViewModel: newsListViewModel, bookmarksViewModel: bookmarksListViewModel) .tabItem { Image(systemName: "square.3.layers.3d.down.left") } @@ -59,7 +60,7 @@ struct MainTabView: View { } #Preview { - MainTabView(newsListViewModel: NewsListViewModel()) + MainTabView(newsListViewModel: NewsListViewModel(), bookmarksListViewModel: BookmarksListViewModel()) .environmentObject(PathModel()) // .environmentObject(NewsListViewModel()) } diff --git a/iOS/RollTheDice/RollTheDice/Source/View/News/NewsList/NewsListView.swift b/iOS/RollTheDice/RollTheDice/Source/View/News/NewsList/NewsListView.swift index d95bb351..45dd6652 100644 --- a/iOS/RollTheDice/RollTheDice/Source/View/News/NewsList/NewsListView.swift +++ b/iOS/RollTheDice/RollTheDice/Source/View/News/NewsList/NewsListView.swift @@ -10,6 +10,7 @@ import SwiftUI struct NewsListView: View { var newsListViewModel: NewsListViewModel + var bookmarksViewModel: BookmarksListViewModel var newsId: Int? @State var selectedIndex: Int = 0 @@ -18,7 +19,7 @@ struct NewsListView: View { ZStack { Color.backgroundDark.ignoresSafeArea(.all) - NewsListContentView(newsList: newsListViewModel.newsList ?? []) + NewsListContentView(newsList: newsListViewModel.newsList ?? [], bookmarksViewModel: bookmarksViewModel) } .task { newsListViewModel.getAllNewsData(page: 0, size: 10) @@ -27,6 +28,7 @@ struct NewsListView: View { private struct NewsListContentView: View { var newsList: [NewsList] + var bookmarksViewModel: BookmarksListViewModel fileprivate var body: some View { @@ -35,7 +37,7 @@ struct NewsListView: View { LazyHStack { ForEach(newsList) { news in - NewsView(news: news) + NewsView(news: news, bookmarksViewModel: bookmarksViewModel) .frame(width: proxy.size.width) .scrollTransition(.interactive, axis: .horizontal) { effect, phase in effect @@ -69,6 +71,6 @@ struct NewsListView: View { } #Preview { - NewsListView(newsListViewModel: NewsListViewModel()) + NewsListView(newsListViewModel: NewsListViewModel(), bookmarksViewModel: BookmarksListViewModel()) .previewInterfaceOrientation(.landscapeLeft) } diff --git a/iOS/RollTheDice/RollTheDice/Source/View/News/NewsList/NewsListViewModel.swift b/iOS/RollTheDice/RollTheDice/Source/View/News/NewsList/NewsListViewModel.swift index 2b9cbfbb..6948b661 100644 --- a/iOS/RollTheDice/RollTheDice/Source/View/News/NewsList/NewsListViewModel.swift +++ b/iOS/RollTheDice/RollTheDice/Source/View/News/NewsList/NewsListViewModel.swift @@ -60,7 +60,6 @@ extension NewsListViewModel { print("news 조회 성공") break case .failure(let error): -// print(error.localizedDescription) Log.network("network error", error.localizedDescription) } }, receiveValue: { [weak self] response in @@ -70,22 +69,3 @@ extension NewsListViewModel { }) } } - - -//public class NewsListViewModel: ObservableObject { -// // News = [NewsModel] -// @Published var newsList: [News] = [] -//// var currentPage = 0 -// -// init( -// newsList: [News] = [ -// -// .init(title: "네이버, 사우디 'LEAP 2024'서 AI·로봇 등 자사 기술력 뽐낸다", postDate: "2023년3월3일", image: "https://imgnews.pstatic.net/image/008/2024/03/05/0005007355_001_20240305100101016.jpg?type=w647", content: "네이버(NAVER)는 사우디아라비아에서 지난 4일부터 7일까지 열리는 글로벌 IT전시회 LEAP 2024에서 AI(인공지능), 클라우드, 로봇 등 자사 핵심 기술을 선보이고, 글로벌 업체들과 비즈니스 협력을 강화한다고 5일 밝혔다.", isBookmarked: false), -// .init(title: "앤스로픽 최신 AI 모델 '클로드3' 출시", postDate: "2023년3월13일", image: "https://imgnews.pstatic.net/image/014/2024/03/05/0005151141_001_20240305101610000.jpg?type=w647", content: "오픈AI의 대항마 앤스로픽이 생성형 인공지능(AI) 최신 모델 '클로드'(Claude)3를 선보이면서 생성형 AI 주도권을 잡기 위한 경쟁이 다시 뜨거워지고 있다. 앤스로픽은 지난 한 해 동안 구글과 세일즈포스, 아마존 등에서 총 73억 달러(약 9조7309억 원)를 투자받고 '클로드3'를 내놨는데 오픈AI의 챗GPT-4를 능가한다고 도발했다.", isBookmarked: true), -// .init(title: "SK C&C, 외부 전문가 대거 영입… “신성장 동력 강화”", postDate: "2023년2월13일", image: "https://imgnews.pstatic.net/image/366/2024/03/05/0000975131_001_20240305093504301.jpg?type=w647", content: "SK C&C는 국내외 신성장 동력 강화를 위해 인공지능(AI)·클라우드·디지털 팩토리·ESG(환경·사회·지배구조) 등 4대 성장 사업과 디지털 컨설팅 중심으로 외부 전문가를 대거 영입해 전진 배치했다고 5일 밝혔다.", isBookmarked: false), -// .init(title: "NHN, 작년 영업익 555억원...전년비 42% ↑", postDate: "2023년2월13일", image: "https://cdnimage.dailian.co.kr/news/202402/news_1707866329_1327972_m_1.png", content: "2NHN은 연결기준 지난해 영업이익이 555억원으로 전년 대비 42.2% 증가했다고 14일 밝혔다.같은 기간 매출은 7.3% 증가한 2조2696억원으로 연간 최대치를 기록했다. 작년 4분기 매출은 5983억원으로 전년 동기 대비 6.7% 올랐다. 반면 영업손실은 78억원으로 적자전환했다. 커머스 부문의 장기 미회수채권 대손상각비 인식과 기술 부문의 기 인식 매출 차감 등 일회성 요인이 영향을 미쳤다.", isBookmarked: false), -// ] -// ) { -// self.newsList = newsList -// } -//} diff --git a/iOS/RollTheDice/RollTheDice/Source/View/News/NewsList/NewsView.swift b/iOS/RollTheDice/RollTheDice/Source/View/News/NewsList/NewsView.swift index 81e81d9d..d346735d 100644 --- a/iOS/RollTheDice/RollTheDice/Source/View/News/NewsList/NewsView.swift +++ b/iOS/RollTheDice/RollTheDice/Source/View/News/NewsList/NewsView.swift @@ -10,12 +10,9 @@ import SwiftUI struct NewsView: View { @EnvironmentObject var pathModel : PathModel - -// var newsListViewModel: NewsListViewModel + var news: NewsList -// var isVisibleView: Bool = true -// var cardWidth: Double = 0.0 -// var cardHeight: Double = 0.0 + var bookmarksViewModel: BookmarksListViewModel var body: some View { VStack(alignment: .center, spacing: 20) { @@ -43,49 +40,15 @@ struct NewsView: View { .frame(width: 322, height: 160) .clipShape(RoundedRectangle(cornerRadius: 8)) .shadow(color: .black.opacity(0.25), radius: 2, x: 0, y: 0) -// case .empty: -// Image(.exampleNews) -// .resizable() -// .aspectRatio(contentMode: .fill) -// .frame(width: 322, height: 160) -// .clipShape(RoundedRectangle(cornerRadius: 8)) -// .shadow(color: .black.opacity(0.25), radius: 2, x: 0, y: 0) -// case .failure(_): -// Image(.exampleNews) -// .resizable() -// .aspectRatio(contentMode: .fill) -// .frame(width: 322, height: 160) -// .clipShape(RoundedRectangle(cornerRadius: 8)) -// .shadow(color: .black.opacity(0.25), radius: 2, x: 0, y: 0) + @unknown default: Text(""); } } - - -// if isVisibleView { -// Button { -// let newsId:Int = news.newsId -// print("!!!!!!!!!!! newsID : \(news.newsId)") -// pathModel.paths.append(.detailNewsView(newsId: newsId)) -// } label: { -// Text("더보기") -// .font(.pretendardBold14) -// .foregroundStyle(.gray01) -// .padding(.horizontal, 30) -// .padding(.vertical, 13) -// .background(.primary01) -// .clipShape( -// RoundedRectangle(cornerRadius: 16) -// ) -// .shadow(color: .basicBlack.opacity(0.25), radius: 2, x: 0, y: 0) -// -// } -// } + Button { let newsId:Int = news.newsId - print("!!!!!!!!!!! newsID : \(news.newsId)") pathModel.paths.append(.detailNewsView(newsId: newsId)) } label: { Text("더보기") @@ -107,7 +70,6 @@ struct NewsView: View { .padding(.top, 50) .frame(width: 380) .background( -// LinearGradient(colors: isVisibleView ? [.basicWhite, .primaryLight01] : [.basicWhite], startPoint: .top, endPoint: .bottom) LinearGradient(colors: [.basicWhite, .primaryLight01], startPoint: .top, endPoint: .bottom) ) .clipShape(RoundedRectangle(cornerRadius: 12)) @@ -117,9 +79,15 @@ struct NewsView: View { HStack { Spacer() Button { - + print("북마크 버튼눌림") + print(news.isBookmarked) + if news.isBookmarked ?? false { + bookmarksViewModel.deleteBookmark(newsId: news.newsId) + } else { + bookmarksViewModel.saveBookmark(newsId: news.newsId) + } } label: { - Image(.bookmarkfill) + Image(news.isBookmarked ?? false ? .bookmarkfill : .bookmarkunfill) .shadow(color: .gray06, radius: 2) } .offset(CGSize(width: -20, height: -10)) @@ -131,12 +99,6 @@ struct NewsView: View { } #Preview { - NewsView(news: .init(newsId: 1, title: "1분기 선방한 dddddddd韓게임사들…엔씨만 울었다", content: "내용내용ㄴㅇㅇㅇ", thumbnailUrl: "", postDate: "2222-22-22", isBookmarked: true)) + NewsView(news: .init(newsId: 1, title: "1분기 선방한 dddddddd韓게임사들…엔씨만 울었다", content: "내용내용ㄴㅇㅇㅇ", thumbnailUrl: "", postDate: "2222-22-22", isBookmarked: true), bookmarksViewModel: BookmarksListViewModel()) .environmentObject(PathModel()) } -//#Preview(traits: .sizeThatFitsLayout) { -// NewsView(news: .init(title: "NHN, 작년 영업익 555억원...전년비 42%", postDate: "2023년2월13일", image: "https://cdnimage.dailian.co.kr/news/202402/news_1707866329_1327972_m_1.png", content: "2NHN은 연결기준 지난해 영업이익이 555억원으로 전년 대비 42.2% 증가했다고 14일 밝혔다.같은 기간 매출은 7.3% 증가한 2조2696억원으로 연간 최대치를 기록했다. 작년 4분기 매출은 5983억원으로 전년 동기 대비 6.7% 올랐다. 반면 영업손실은 78억원으로 적자전환했다. 커머스 부문의 장기 미회수채권 대손상각비 인식과 기술 부문의 기 인식 매출 차감 등 일회성 요인이 영향을 미쳤다.", isBookmarked: false)) -// .previewInterfaceOrientation(.landscapeLeft) -// .previewLayout(.sizeThatFits) -// .colorScheme(.dark) -//} diff --git a/iOS/RollTheDice/RollTheDice/Source/View/Report/Daily/DailyBarChartView.swift b/iOS/RollTheDice/RollTheDice/Source/View/Report/Daily/DailyBarChartView.swift index 8f026af7..2b02f12c 100644 --- a/iOS/RollTheDice/RollTheDice/Source/View/Report/Daily/DailyBarChartView.swift +++ b/iOS/RollTheDice/RollTheDice/Source/View/Report/Daily/DailyBarChartView.swift @@ -11,7 +11,7 @@ import Charts struct DailyBarChartView: View { - @StateObject var dailyViewModel: DailyReportViewModel + var dailyViewModel: DailyReportViewModel @State var selectedDay: Date? @State var selectedView: Int? @@ -21,9 +21,9 @@ struct DailyBarChartView: View { var selectedValue: (date: Date, views: Int)? { if let selectedDay { - for preview in dailyViewModel.dailyReportList.reportList { + for preview in dailyViewModel.dailyReportList ?? [] { if preview.date.formatted(date: .long, time: .omitted) == selectedDay.formatted(date: .long, time: .omitted) { - return (selectedDay, preview.views) + return (selectedDay, preview.views!) } } } @@ -57,10 +57,10 @@ struct DailyBarChartView: View { Chart{ - ForEach(dailyViewModel.dailyReportList.reportList) { day in + ForEach(dailyViewModel.dailyReportList ?? []) { day in BarMark( x: .value("Day", day.date, unit: .weekdayOrdinal), - y: .value("Views", day.views) + y: .value("Views", day.views ?? 0) ) .cornerRadius(8) @@ -100,6 +100,9 @@ struct DailyBarChartView: View { } .chartYAxis(.hidden) .chartXSelection(value: $selectedDay) + .task { + dailyViewModel.getDailyViews() + } } } diff --git a/iOS/RollTheDice/RollTheDice/Source/View/Report/Daily/DailyReportModel.swift b/iOS/RollTheDice/RollTheDice/Source/View/Report/Daily/DailyReportModel.swift index 9a5051cc..10a34175 100644 --- a/iOS/RollTheDice/RollTheDice/Source/View/Report/Daily/DailyReportModel.swift +++ b/iOS/RollTheDice/RollTheDice/Source/View/Report/Daily/DailyReportModel.swift @@ -8,24 +8,25 @@ import Foundation import SwiftUI -struct DailyReportList: Hashable { - var reportList: [DailyReport] -} +//struct DailyReportList: Codable { +// var reportList: [DailyReport] +//} -struct DailyReport: Hashable, Identifiable { - let id = UUID() - let dateStr: String // DateFormatter로 변환 - let views: Int +struct DailyReport: Codable, Identifiable { + + let id = UUID().uuidString + let views: Int? + let dateTime: String? var date: Date { // TODO: Format Style 사용해서 String -> Date 생성하기 // let strategy = Date.ParseStrategy(format: "\(month: .twoDigits)년 \(month: .twoDigits)월 \(day: .defaultDigits)", timeZone: TimeZone(abbreviation: "CDT")!) let dateFormatter = DateFormatter() - dateFormatter.dateFormat = "yyyy년 MM월 dd일" + dateFormatter.dateFormat = "yyyy-MM-dd" - let convertedDate = dateFormatter.date(from: dateStr)! + let convertedDate = dateFormatter.date(from: dateTime ?? "0000-00-00") - return convertedDate + return convertedDate! } } diff --git a/iOS/RollTheDice/RollTheDice/Source/View/Report/Daily/DailyReportViewModel.swift b/iOS/RollTheDice/RollTheDice/Source/View/Report/Daily/DailyReportViewModel.swift index 344ca3bb..b9aa5374 100644 --- a/iOS/RollTheDice/RollTheDice/Source/View/Report/Daily/DailyReportViewModel.swift +++ b/iOS/RollTheDice/RollTheDice/Source/View/Report/Daily/DailyReportViewModel.swift @@ -6,34 +6,62 @@ // import Foundation +import Combine +import CombineMoya +import Moya +import SwiftUI -class DailyReportViewModel: ObservableObject{ - @Published var dailyReportList: DailyReportList +@Observable class DailyReportViewModel { + var dailyReportList: [DailyReport]? /// 일주일 평균 조회수 var averageView: String { var aver = 0.0 - for daily in dailyReportList.reportList { - aver += Double(daily.views) + for daily in dailyReportList ?? [] { + aver += Double(daily.views ?? 0) } return String(format: "%.1f", aver / 7) } - init( - dailyReportList: DailyReportList = .init( - reportList: - [.init(dateStr: "2024년 1월 1일", views: 32), - .init(dateStr: "2024년 1월 2일", views: 2), - .init(dateStr: "2024년 1월 3일", views: 300), - .init(dateStr: "2024년 1월 4일", views: 999), - .init(dateStr: "2024년 1월 5일", views: 12), - .init(dateStr: "2024년 1월 6일", views: 1), - .init(dateStr: "2024년 1월 7일", views: 99), - ] + var dailyCancellable: AnyCancellable? + + let provider = MoyaProvider(plugins: [MoyaLoggingPlugin()]) + + func dailyToViewModel(_ list: [DailyReport]) { + self.dailyReportList = list + } +} + +extension DailyReportViewModel { + + public func getDailyViews() { + guard let accessToken = TokenManager.shared.accessToken else { + print("Access token 사용 불가능...") + return + } + + if let cancellable = dailyCancellable { + cancellable.cancel() + } + + dailyCancellable = provider.requestWithProgressPublisher( + .recentSevenPerDate(accessToken: accessToken) ) - ) { - self.dailyReportList = dailyReportList + .compactMap { $0.response?.data } + .receive(on: DispatchQueue.main) + .decode(type: [DailyReport].self, decoder: JSONDecoder()) + .sink(receiveCompletion: { result in + switch result { + case .finished: + print("daily report 조회성공") + case .failure(let error): + Log.network("daily report network error", error.localizedDescription) + } + }, receiveValue: { [weak self] response in + self?.dailyToViewModel(response) + print(response) + }) } } diff --git a/iOS/RollTheDice/RollTheDice/Source/View/Report/Type/TypePieChartView.swift b/iOS/RollTheDice/RollTheDice/Source/View/Report/Type/TypePieChartView.swift index bbd26054..7a2e0399 100644 --- a/iOS/RollTheDice/RollTheDice/Source/View/Report/Type/TypePieChartView.swift +++ b/iOS/RollTheDice/RollTheDice/Source/View/Report/Type/TypePieChartView.swift @@ -10,25 +10,24 @@ import Charts struct TypePieChartView: View { - @StateObject var reportViewModel: TypeReportViewModel + var reportViewModel: TypeReportViewModel var isPreview: Bool = false var mostViewed: NewsType { - return reportViewModel.sortedList.first!.newsType + return reportViewModel.sortedList.first?.category ?? .economy } var body: some View { Chart(reportViewModel.sortedList) { report in SectorMark( - angle: .value("Views", report.view), + angle: .value("Views", report.views ?? 0), innerRadius: .ratio(0.7), angularInset: 2.0 ) .cornerRadius(8) - .foregroundStyle(report.newsType.color.gradient) + .foregroundStyle(report.category?.color.gradient ?? Color.primary01.gradient) } - /// pie chart의 가운데 문구 .chartBackground { chartProxy in GeometryReader { geometry in let frame = geometry[chartProxy.plotFrame!] @@ -47,6 +46,9 @@ struct TypePieChartView: View { } } .padding(100) + .task { + reportViewModel.getTypeReport() + } } } diff --git a/iOS/RollTheDice/RollTheDice/Source/View/Report/Type/TypeReportModel.swift b/iOS/RollTheDice/RollTheDice/Source/View/Report/Type/TypeReportModel.swift index 1c35be6e..2c8c56a1 100644 --- a/iOS/RollTheDice/RollTheDice/Source/View/Report/Type/TypeReportModel.swift +++ b/iOS/RollTheDice/RollTheDice/Source/View/Report/Type/TypeReportModel.swift @@ -7,19 +7,9 @@ import Foundation -struct TypeReportList: Hashable { - var reportList: [TypeReport] -} - -struct TypeReport: Hashable, Identifiable { - var id = UUID() - var newsType: NewsType - var view: Int - - // case politics // 정치 - // case economy // 경제 - // case society // 사회 - // case living // 생활/문화 - // case world // 세계 - // case science // IT/과학 +struct TypeReport: Codable, Identifiable { + + let id = UUID().uuidString + let category: NewsType? + let views: Int? } diff --git a/iOS/RollTheDice/RollTheDice/Source/View/Report/Type/TypeReportViewModel.swift b/iOS/RollTheDice/RollTheDice/Source/View/Report/Type/TypeReportViewModel.swift index d23bce97..fcc6bfde 100644 --- a/iOS/RollTheDice/RollTheDice/Source/View/Report/Type/TypeReportViewModel.swift +++ b/iOS/RollTheDice/RollTheDice/Source/View/Report/Type/TypeReportViewModel.swift @@ -6,28 +6,61 @@ // import Foundation +import Combine +import CombineMoya +import Moya +import SwiftUI -class TypeReportViewModel: ObservableObject { +@Observable class TypeReportViewModel { - @Published var reportList: TypeReportList + var typeReportList: [TypeReport]? + + // 비율이 낮은 순으로 정렬 (파이 차트에서 반시계방향으로 그래프 차지) + var sortedList: [TypeReport] { + if let reportList = typeReportList { + return reportList.sorted { ($0.views ?? 0) < ($1.views ?? 0) } + } else { + return [] + } + } - init( - reportList: TypeReportList = .init( - reportList: [ - .init(newsType: .economy, view: 10), - .init(newsType: .living, view: 20), - .init(newsType: .politics, view: 30), - .init(newsType: .science, view: 5), - .init(newsType: .society, view: 5), - .init(newsType: .world, view: 30) - ] - ) - ) { - self.reportList = reportList + var typeCancellable: AnyCancellable? + + let provider = MoyaProvider(plugins: [MoyaLoggingPlugin()]) + + func typeToViewModel(_ list: [TypeReport]) { + self.typeReportList = list } +} + +extension TypeReportViewModel { - // 비율이 낮은 순으로 정렬 (파이 차트에서 반시계방향으로 그래프 차지) - var sortedList: [TypeReport] { - return reportList.reportList.sorted { $0.view > $1.view } + public func getTypeReport() { + guard let accessToken = TokenManager.shared.accessToken else { + print("Access token 사용 불가능...") + return + } + + if let cancellable = typeCancellable { + cancellable.cancel() + } + + typeCancellable = provider.requestWithProgressPublisher( + .statisticsCategory(accessToken: accessToken) + ) + .compactMap { $0.response?.data } + .receive(on: DispatchQueue.main) + .decode(type: [TypeReport].self, decoder: JSONDecoder()) + .sink(receiveCompletion: { result in + switch result { + case .finished: + print("type report 조회성공") + case .failure(let error): + Log.network("type report network error", error.localizedDescription) + } + }, receiveValue: { [weak self] response in + self?.typeToViewModel(response) + print(response) + }) } }