From 7e3f1edd5367b555345a2a728c1d13706daab758 Mon Sep 17 00:00:00 2001 From: Chandram-Dutta Date: Mon, 4 Mar 2024 19:06:40 +0530 Subject: [PATCH] feat: converted timetable api to async/await --- .../Service/TimeTableAPIService.swift | 41 ++---- .../ViewModel/TimeTableViewModel.swift | 29 ++-- .../VITTY/TimeTable/Views/TimeTableView.swift | 128 ++++++++++-------- 3 files changed, 99 insertions(+), 99 deletions(-) diff --git a/VITTY/VITTY/TimeTable/Service/TimeTableAPIService.swift b/VITTY/VITTY/TimeTable/Service/TimeTableAPIService.swift index 938e1a6..a91f120 100644 --- a/VITTY/VITTY/TimeTable/Service/TimeTableAPIService.swift +++ b/VITTY/VITTY/TimeTable/Service/TimeTableAPIService.swift @@ -7,45 +7,22 @@ import Foundation -enum TimeTableAPIServiceError: Error { - case invalidUrl -} - class TimeTableAPIService { static let shared = TimeTableAPIService() func getTimeTable( with username: String, - authToken: String, - completion: @escaping (Result) -> Void - ) { - guard let url = URL(string: "\(Constants.url)timetable/\(username)") else { - completion(.failure(TimeTableAPIServiceError.invalidUrl)) - return - } + authToken: String + ) async throws -> TimeTable { + + let url = URL(string: "\(Constants.url)timetable/\(username)")! var request = URLRequest(url: url) request.httpMethod = "GET" request.setValue("Token \(authToken)", forHTTPHeaderField: "Authorization") - let task = URLSession.shared.dataTask(with: request) { data, _, error in - if let error = error { - completion(.failure(error)) - return - } - guard let data = data else { - completion(.failure(TimeTableAPIServiceError.invalidUrl)) - return - } - do { - let decoder = JSONDecoder() - decoder.dateDecodingStrategy = .iso8601 - let timetableRaw = try decoder.decode(TimeTableRaw.self, from: data) - completion(.success(timetableRaw.data)) - } - catch { - completion(.failure(error)) - return - } - } - task.resume() + let data = try await URLSession.shared.data(for: request) + let decoder = JSONDecoder() + decoder.dateDecodingStrategy = .iso8601 + let timeTableRaw = try decoder.decode(TimeTableRaw.self, from: data.0) + return timeTableRaw.data } } diff --git a/VITTY/VITTY/TimeTable/ViewModel/TimeTableViewModel.swift b/VITTY/VITTY/TimeTable/ViewModel/TimeTableViewModel.swift index 5f63367..1b4c9f8 100644 --- a/VITTY/VITTY/TimeTable/ViewModel/TimeTableViewModel.swift +++ b/VITTY/VITTY/TimeTable/ViewModel/TimeTableViewModel.swift @@ -8,14 +8,19 @@ import Foundation import SwiftData +public enum Stage { + case loading + case error + case data +} + extension TimeTableView { @Observable class TimeTableViewModel { var timeTable: TimeTable? - var isLoading: Bool = false - var error: String? + var stage: Stage = .loading var lectures = [Lecture]() var dayNo = Date.convertToMondayWeek() @@ -40,16 +45,16 @@ extension TimeTableView { } } - func fetchTimeTable(username: String, authToken: String) { - TimeTableAPIService.shared.getTimeTable(with: username, authToken: authToken) { - [weak self] result in - switch result { - case let .success(response): - self?.timeTable = response - self?.changeDay() - case let .failure(response): - print("Error: \(response)") - } + func fetchTimeTable(username: String, authToken: String) async { + do { + stage = .loading + let data = try await TimeTableAPIService.shared.getTimeTable(with: username, authToken: authToken) + timeTable = data + changeDay() + stage = .data + } catch { + print(error) + stage = .error } } } diff --git a/VITTY/VITTY/TimeTable/Views/TimeTableView.swift b/VITTY/VITTY/TimeTable/Views/TimeTableView.swift index e9cdb57..6001e94 100644 --- a/VITTY/VITTY/TimeTable/Views/TimeTableView.swift +++ b/VITTY/VITTY/TimeTable/Views/TimeTableView.swift @@ -12,8 +12,6 @@ struct TimeTableView: View { @Environment(AuthViewModel.self) private var authViewModel private let daysOfWeek = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"] - @State private var showingSheet = false - @State private var viewModel = TimeTableViewModel() @State private var selectedLecture: Lecture? = nil @@ -22,62 +20,84 @@ struct TimeTableView: View { var body: some View { NavigationStack { ZStack { - Image("HomeBG") + Image(viewModel.lectures == [] ? "HomeNoClassesBG" : "HomeBG") .resizable() .ignoresSafeArea() - VStack { - ScrollView(.horizontal) { - HStack { - ForEach(daysOfWeek, id: \.self) { day in - Text(day) - .frame(width: 60, height: 54) - .background( - daysOfWeek[viewModel.dayNo] == day - ? Color(Color.theme.secondary) : Color.clear - ) - .onTapGesture { - withAnimation { - viewModel.dayNo = daysOfWeek.firstIndex(of: day)! - viewModel.changeDay() - } - } - .clipShape(RoundedRectangle(cornerRadius: 10)) - } - } + switch viewModel.stage { + case .loading: + VStack { + Spacer() + ProgressView() + Spacer() + } + case .error: + VStack{ + Spacer() + Text("Error") + Spacer() } - .scrollIndicators(.hidden) - .background(Color("DarkBG")) - .clipShape(RoundedRectangle(cornerRadius: 10)) - .padding(.horizontal) - List(viewModel.lectures.sorted()) { lecture in - VStack(alignment: .leading) { - Text(lecture.name) - .font(.headline) + case .data: + VStack { + ScrollView(.horizontal) { HStack { - Text( - "\(formatTime(time: lecture.startTime)) - \(formatTime(time: lecture.endTime))" - ) - Spacer() - Text("\(lecture.venue)") + ForEach(daysOfWeek, id: \.self) { day in + Text(day) + .frame(width: 60, height: 54) + .background( + daysOfWeek[viewModel.dayNo] == day + ? Color(Color.theme.secondary) : Color.clear + ) + .onTapGesture { + withAnimation { + viewModel.dayNo = daysOfWeek.firstIndex(of: day)! + viewModel.changeDay() + } + } + .clipShape(RoundedRectangle(cornerRadius: 10)) + } } - .foregroundColor(Color.vprimary) - .font(.caption) } - .onTapGesture { - selectedLecture = lecture + .scrollIndicators(.hidden) + .background(Color("DarkBG")) + .clipShape(RoundedRectangle(cornerRadius: 10)) + .padding(.horizontal) + if viewModel.lectures == [] { + Spacer() + Text("No classes today!") + .font(Font.custom("Poppins-Bold", size: 24)) + Text(StringConstants.noClassQuotesOffline.randomElement()!) + } else { + List(viewModel.lectures.sorted()) { lecture in + VStack(alignment: .leading) { + Text(lecture.name) + .font(.headline) + HStack { + Text( + "\(formatTime(time: lecture.startTime)) - \(formatTime(time: lecture.endTime))" + ) + Spacer() + Text("\(lecture.venue)") + } + .foregroundColor(Color.vprimary) + .font(.caption) + } + .onTapGesture { + selectedLecture = lecture + } + .listRowBackground(Color("DarkBG")) + } + .sheet(item: $selectedLecture) { lecture in + LectureDetailView(lecture: lecture) + } + .scrollContentBackground(.hidden) } - .listRowBackground(Color("DarkBG")) - } - .sheet(item: $selectedLecture) { lecture in - LectureDetailView(lecture: lecture) + Spacer() } - .scrollContentBackground(.hidden) - Spacer() } + } .navigationTitle(friend?.name ?? "Schedule") .toolbar { - Menu { if friend == nil { NavigationLink { @@ -96,13 +116,11 @@ struct TimeTableView: View { string: "\(APIConstants.base_url)/api/v2/friends/\(friend?.username ?? "")" )! var request = URLRequest(url: url) - request.httpMethod = "DELETE" request.addValue( "Token \(authViewModel.appUser?.token ?? "")", forHTTPHeaderField: "Authorization" ) - let task = URLSession.shared.dataTask(with: request) { (data, response, error) in if let error = error { @@ -122,15 +140,15 @@ struct TimeTableView: View { width: 40 ) } - - } } - .task { - viewModel.fetchTimeTable( - username: friend?.username ?? (authViewModel.appUser?.username ?? ""), - authToken: authViewModel.appUser?.token ?? "" - ) + .onAppear { + Task { + await viewModel.fetchTimeTable ( + username: friend?.username ?? (authViewModel.appUser?.username ?? ""), + authToken: authViewModel.appUser?.token ?? "" + ) + } } }