From 18a01ab349b615d0529bce6cae722bb7cbda1a15 Mon Sep 17 00:00:00 2001 From: "emre.kartal" Date: Sun, 14 Apr 2024 17:09:48 +0200 Subject: [PATCH] Added codeable protocol to classes to deserialize API json objects :hammer: --- .../AllInApp/AllIn/Components/BetCard.swift | 4 +- .../AllIn/Components/BetLineLoading.swift | 8 +- ...tionCell.swift => ParticipationCell.swift} | 10 +- .../AllIn/Components/ReviewCard.swift | 6 +- .../AllInApp/AllIn/Services/AuthService.swift | 34 +++--- .../ViewModels/CreationBetViewModel.swift | 4 +- .../AllIn/Views/BetEndingValidationView.swift | 4 +- .../AllInApp/AllIn/Views/DetailsView.swift | 6 +- .../AllInApp.xcodeproj/project.pbxproj | 8 +- .../Sources/Api/Factory/FactoryApiBet.swift | 111 ++++-------------- .../Model/Sources/Model/AnswerDetail.swift | 18 +-- Sources/Model/Sources/Model/Bet.swift | 51 +++++--- Sources/Model/Sources/Model/BetDetail.swift | 8 +- .../Model/Sources/Model/Enums/BetStatus.swift | 9 +- .../Sources/Model/Factory/FactoryBet.swift | 1 - Sources/Model/Sources/Model/MatchBet.swift | 24 +++- .../Model/Sources/Model/Participation.swift | 17 ++- Sources/Model/Sources/Model/User.swift | 30 +---- Sources/StubLib/Sources/StubLib/Stub.swift | 24 ++-- 19 files changed, 152 insertions(+), 225 deletions(-) rename Sources/AllInApp/AllIn/Components/{ParticiationCell.swift => ParticipationCell.swift} (78%) diff --git a/Sources/AllInApp/AllIn/Components/BetCard.swift b/Sources/AllInApp/AllIn/Components/BetCard.swift index a00faea..9eefbf8 100644 --- a/Sources/AllInApp/AllIn/Components/BetCard.swift +++ b/Sources/AllInApp/AllIn/Components/BetCard.swift @@ -19,7 +19,7 @@ struct BetCard: View { VStack(alignment: .leading,spacing: 2){ HStack{ Spacer() - Text("proposé par " + bet.author.username.capitalized) + Text("proposé par " + bet.author.capitalized) .font(.system(size: 10)) .foregroundColor(AllInColors.grey800Color) @@ -82,7 +82,7 @@ struct BetCard_Previews: PreviewProvider { isPublic: true, status: .inProgress, invited: [], - author: User(username: "Imri", email: "emre.kartal@etu.uca.fr", nbCoins: 75, friends: []), + author: "Imri", registered: [])) .preferredColorScheme(.dark) } diff --git a/Sources/AllInApp/AllIn/Components/BetLineLoading.swift b/Sources/AllInApp/AllIn/Components/BetLineLoading.swift index fec31aa..1fe447a 100644 --- a/Sources/AllInApp/AllIn/Components/BetLineLoading.swift +++ b/Sources/AllInApp/AllIn/Components/BetLineLoading.swift @@ -15,8 +15,8 @@ struct BetLineLoading: View { var value: CGFloat { let totalParticipations = participations.count - let numberOfYes = participations.filter { $0.response.uppercased() == "YES" }.count - let numberOfNo = participations.filter { $0.response.uppercased() == "NO" }.count + let numberOfYes = participations.filter { $0.answer.uppercased() == "YES" }.count + let numberOfNo = participations.filter { $0.answer.uppercased() == "NO" }.count if(numberOfNo == 0 && numberOfYes == 0){ return 0.5 } @@ -25,11 +25,11 @@ struct BetLineLoading: View { } var yesParticipations: [Participation] { - participations.filter { $0.response.uppercased() == "YES" } + participations.filter { $0.answer.uppercased() == "YES" } } var noParticipations: [Participation] { - participations.filter { $0.response.uppercased() == "NO" } + participations.filter { $0.answer.uppercased() == "NO" } } var body: some View { diff --git a/Sources/AllInApp/AllIn/Components/ParticiationCell.swift b/Sources/AllInApp/AllIn/Components/ParticipationCell.swift similarity index 78% rename from Sources/AllInApp/AllIn/Components/ParticiationCell.swift rename to Sources/AllInApp/AllIn/Components/ParticipationCell.swift index 5f0ae70..60902b1 100644 --- a/Sources/AllInApp/AllIn/Components/ParticiationCell.swift +++ b/Sources/AllInApp/AllIn/Components/ParticipationCell.swift @@ -1,5 +1,5 @@ // -// ParticiationCell.swift +// ParticipationCell.swift // AllIn // // Created by Lucas Delanier on 21/01/2024. @@ -8,20 +8,20 @@ import SwiftUI import Model -struct ParticiationCell: View { - @State var participation: Participation? +struct ParticipationCell: View { + @State var participation: Participation var body: some View { HStack(alignment: .center, spacing: 0){ Circle() .frame(width: 30, height: 30) .foregroundColor(AllInColors.grey700Color) .padding(.trailing, 7) - Text(participation?.user.username ?? "Unknown") + Text(participation.username) .font(.system(size: 15)) .foregroundStyle(AllInColors.primaryTextColor) .fontWeight(.semibold) Spacer() - Text(participation?.stake.description ?? "NaN") + Text(participation.stake.description) .font(.system(size: 18)) .foregroundStyle(AllInColors.lightPurpleColor) .fontWeight(.bold) diff --git a/Sources/AllInApp/AllIn/Components/ReviewCard.swift b/Sources/AllInApp/AllIn/Components/ReviewCard.swift index 0521c32..fb356c6 100644 --- a/Sources/AllInApp/AllIn/Components/ReviewCard.swift +++ b/Sources/AllInApp/AllIn/Components/ReviewCard.swift @@ -22,7 +22,7 @@ struct ReviewCard: View { VStack(alignment: .leading,spacing: 2){ HStack{ Spacer() - Text("proposé par \(betDetail.bet.author.username)") + Text("proposé par \(betDetail.bet.author)") .font(.system(size: 10)) .foregroundColor(AllInColors.grey800Color) @@ -44,7 +44,7 @@ struct ReviewCard: View { VStack(alignment: .center,spacing:0){ HStack(){ Spacer() - if(betDetail.bet.isFinish()){ + if(betDetail.bet.endBetDate < Date()){ Text("Terminé") .foregroundColor(.white) .font(.system(size: 25)) @@ -72,7 +72,7 @@ struct ReviewCard: View { .frame(width: .infinity) .padding(.all,2) .background( - isAWin || betDetail.bet.isFinish() ? + isAWin || betDetail.bet.endBetDate < Date() ? AnyView(AllInColors.primaryGradient) : AnyView(Color.black) ) .cornerRadius(20, corners: [.bottomLeft,.bottomRight]) diff --git a/Sources/AllInApp/AllIn/Services/AuthService.swift b/Sources/AllInApp/AllIn/Services/AuthService.swift index 671c7d0..133dfd4 100644 --- a/Sources/AllInApp/AllIn/Services/AuthService.swift +++ b/Sources/AllInApp/AllIn/Services/AuthService.swift @@ -20,12 +20,12 @@ class AuthService: IAuthService { var request = URLRequest(url: url) request.httpMethod = "POST" request.setValue("application/json", forHTTPHeaderField: "Content-Type") - + let json = [ "login": login.lowercased(), "password": password, ] - + if let jsonData = try? JSONSerialization.data(withJSONObject: json, options: []) { URLSession.shared.uploadTask(with: request, from: jsonData) { data, response, error in print("ALLIN: Process LOGIN") @@ -53,7 +53,7 @@ class AuthService: IAuthService { }.resume() } } - + func register(username: String, email: String, password: String, completion : @escaping (Int)-> ()) { let url = URL(string: Config.allInApi + "/users/register")! @@ -100,7 +100,7 @@ class AuthService: IAuthService { guard let token = AppStateContainer.shared.authenticationRefresh else { return } - + let url = URL(string: Config.allInApi + "users/token")! var request = URLRequest(url: url) request.httpMethod = "GET" @@ -110,14 +110,11 @@ class AuthService: IAuthService { URLSession.shared.dataTask(with: request) { data, response, error in if let data = data, let httpResponse = response as? HTTPURLResponse { - if httpResponse.statusCode == 200 { - if let userJson = try? JSONSerialization.jsonObject(with: data, options: []) as? [String: Any], - let user = User.mapUser(from: userJson) { - AppStateContainer.shared.user = user - AppStateContainer.shared.loggedState.connectedUser = true - WidgetCenter.shared.reloadAllTimelines() - self.initManagerVM(token: token) - } + if httpResponse.statusCode == 200, let user = try? JSONDecoder().decode(User.self, from: data) { + AppStateContainer.shared.user = user + AppStateContainer.shared.loggedState.connectedUser = true + WidgetCenter.shared.reloadAllTimelines() + self.initManagerVM(token: token) } else { AppStateContainer.shared.authenticationRefresh = nil } @@ -133,17 +130,14 @@ class AuthService: IAuthService { request.httpMethod = "GET" request.setValue("application/json", forHTTPHeaderField: "Content-Type") request.setValue("Bearer \(token)", forHTTPHeaderField: "Authorization") - + URLSession.shared.dataTask(with: request) { data, response, error in if let data = data, let httpResponse = response as? HTTPURLResponse { - if httpResponse.statusCode == 200 { - if let userJson = try? JSONSerialization.jsonObject(with: data, options: []) as? [String: Any], - let user = User.mapUser(from: userJson) { - completion(httpResponse.statusCode) - AppStateContainer.shared.user = user - WidgetCenter.shared.reloadAllTimelines() - } + if httpResponse.statusCode == 200, let user = try? JSONDecoder().decode(User.self, from: data) { + AppStateContainer.shared.user = user + WidgetCenter.shared.reloadAllTimelines() + completion(httpResponse.statusCode) } else { completion(httpResponse.statusCode) } diff --git a/Sources/AllInApp/AllIn/ViewModels/CreationBetViewModel.swift b/Sources/AllInApp/AllIn/ViewModels/CreationBetViewModel.swift index 4e70726..e7aeecc 100644 --- a/Sources/AllInApp/AllIn/ViewModels/CreationBetViewModel.swift +++ b/Sources/AllInApp/AllIn/ViewModels/CreationBetViewModel.swift @@ -38,7 +38,7 @@ class CreationBetViewModel: ObservableObject { resetAllFieldErrors() if let user = AppStateContainer.shared.user { - manager.addBet(bet: toBet(theme: theme, description: description, endRegister: endRegisterDate, endBet: endBetDate, isPublic: isPublic, status: .inProgress, creator: user, type: selectedOption)) { statusCode in + manager.addBet(bet: toBet(theme: theme, description: description, endRegister: endRegisterDate, endBet: endBetDate, isPublic: isPublic, status: .inProgress, creator: user.username, type: selectedOption)) { statusCode in switch statusCode { case 201: self.betAdded = true @@ -113,7 +113,7 @@ class CreationBetViewModel: ObservableObject { self.errorMessage = errorMessage } - func toBet(theme: String, description: String, endRegister: Date, endBet: Date, isPublic: Bool, status: BetStatus, creator: User, type: Int) -> Bet { + func toBet(theme: String, description: String, endRegister: Date, endBet: Date, isPublic: Bool, status: BetStatus, creator: String, type: Int) -> Bet { switch type { case 0: return BinaryBet(theme: theme, phrase: description, endRegisterDate: endRegister, endBetDate: endBet, isPublic: isPublic, status: status, invited: [], author: creator, registered: []) diff --git a/Sources/AllInApp/AllIn/Views/BetEndingValidationView.swift b/Sources/AllInApp/AllIn/Views/BetEndingValidationView.swift index a3949d5..997f6a6 100644 --- a/Sources/AllInApp/AllIn/Views/BetEndingValidationView.swift +++ b/Sources/AllInApp/AllIn/Views/BetEndingValidationView.swift @@ -54,6 +54,7 @@ struct BetEndingValidationView: View { Text("Ce bet est arrivé à la date de fin. Vous devez à présent distribuer les gains en validant le pari gagnant.") .textStyle(weight: .regular, color: AllInColors.grey800Color, size: 13) .multilineTextAlignment(.center) + Text("Veuillez choisir la réponse finale:") .font(.system(size: 17)) .foregroundStyle(.white) @@ -61,8 +62,9 @@ struct BetEndingValidationView: View { .padding(.top, 30) .padding(.bottom, 10) .frame(maxWidth: .infinity, alignment: .leading) + VStack(spacing: 14){ - ForEach(bet.answers, id: \.self) { answer in + ForEach(bet.answers) { answer in ChoiceFinalAnswerCell(selected : answer.response == viewModel.selectedAnswer, answer: answer).onTapGesture { if(viewModel.selectedAnswer == answer.response){ viewModel.selectedAnswer = nil diff --git a/Sources/AllInApp/AllIn/Views/DetailsView.swift b/Sources/AllInApp/AllIn/Views/DetailsView.swift index 70bb1c3..fea924d 100644 --- a/Sources/AllInApp/AllIn/Views/DetailsView.swift +++ b/Sources/AllInApp/AllIn/Views/DetailsView.swift @@ -66,7 +66,7 @@ struct DetailsView: View { Text("proposé par") .font(.system(size: 10)) .foregroundColor(AllInColors.grey800Color) - Text((viewModel.betDetail?.bet.author.username ?? "Unknown").capitalized) + Text((viewModel.betDetail?.bet.author ?? "Unknown").capitalized) .font(.system(size: 10)) .fontWeight(.semibold) .foregroundColor(AllInColors.primaryTextColor) @@ -122,8 +122,8 @@ struct DetailsView: View { .fontWeight(.bold) .padding(.bottom, 10) ScrollView(showsIndicators: false) { - ForEach(viewModel.betDetail?.participations ?? []) { (participation: Participation) in - ParticiationCell(participation: participation).padding(.horizontal, 10) + ForEach(viewModel.betDetail?.participations ?? []) { participation in + ParticipationCell(participation: participation).padding(.horizontal, 10) } } .padding(.bottom, geometry.safeAreaInsets.bottom + 28) diff --git a/Sources/AllInApp/AllInApp.xcodeproj/project.pbxproj b/Sources/AllInApp/AllInApp.xcodeproj/project.pbxproj index bbccc8f..3b1a353 100644 --- a/Sources/AllInApp/AllInApp.xcodeproj/project.pbxproj +++ b/Sources/AllInApp/AllInApp.xcodeproj/project.pbxproj @@ -18,7 +18,7 @@ 1244EF622B4EC67000374ABF /* ReviewCard.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1244EF612B4EC67000374ABF /* ReviewCard.swift */; }; 129D051D2B6E7FF0003D3E08 /* OddCapsule.swift in Sources */ = {isa = PBXBuildFile; fileRef = 129D051C2B6E7FF0003D3E08 /* OddCapsule.swift */; }; 12C370482B5A5EE500CD9F0F /* BetLineLoading.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12C370472B5A5EE500CD9F0F /* BetLineLoading.swift */; }; - 12C3704A2B5D5BD000CD9F0F /* ParticiationCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12C370492B5D5BD000CD9F0F /* ParticiationCell.swift */; }; + 12C3704A2B5D5BD000CD9F0F /* ParticipationCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12C370492B5D5BD000CD9F0F /* ParticipationCell.swift */; }; EC0193782B25BF16005D81E6 /* AllcoinsCapsule.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC0193772B25BF16005D81E6 /* AllcoinsCapsule.swift */; }; EC01937A2B25C12B005D81E6 /* BetCard.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC0193792B25C12B005D81E6 /* BetCard.swift */; }; EC01937C2B25C2A8005D81E6 /* AllcoinsCounter.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC01937B2B25C2A8005D81E6 /* AllcoinsCounter.swift */; }; @@ -154,7 +154,7 @@ 1244EF612B4EC67000374ABF /* ReviewCard.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReviewCard.swift; sourceTree = ""; }; 129D051C2B6E7FF0003D3E08 /* OddCapsule.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OddCapsule.swift; sourceTree = ""; }; 12C370472B5A5EE500CD9F0F /* BetLineLoading.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BetLineLoading.swift; sourceTree = ""; }; - 12C370492B5D5BD000CD9F0F /* ParticiationCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ParticiationCell.swift; sourceTree = ""; }; + 12C370492B5D5BD000CD9F0F /* ParticipationCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ParticipationCell.swift; sourceTree = ""; }; EC0193772B25BF16005D81E6 /* AllcoinsCapsule.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AllcoinsCapsule.swift; sourceTree = ""; }; EC0193792B25C12B005D81E6 /* BetCard.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BetCard.swift; sourceTree = ""; }; EC01937B2B25C2A8005D81E6 /* AllcoinsCounter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AllcoinsCounter.swift; sourceTree = ""; }; @@ -420,7 +420,7 @@ 120919172B56D0AE00D0FA29 /* ParticipationModal.swift */, 120919192B56DC6C00D0FA29 /* DropDownAnswerMenu.swift */, 12C370472B5A5EE500CD9F0F /* BetLineLoading.swift */, - 12C370492B5D5BD000CD9F0F /* ParticiationCell.swift */, + 12C370492B5D5BD000CD9F0F /* ParticipationCell.swift */, 123225DA2B67E41400D30BB3 /* ChoiceFinalAnswerCell.swift */, 129D051C2B6E7FF0003D3E08 /* OddCapsule.swift */, ); @@ -693,7 +693,7 @@ EC3077072B24CB840060E34D /* SplashView.swift in Sources */, EC01937E2B25C52E005D81E6 /* TopBar.swift in Sources */, ECA9D1CB2B2DA2320076E0EC /* DropDownFriends.swift in Sources */, - 12C3704A2B5D5BD000CD9F0F /* ParticiationCell.swift in Sources */, + 12C3704A2B5D5BD000CD9F0F /* ParticipationCell.swift in Sources */, 123590B82B5541BA00F7AEBD /* ParticipateButton.swift in Sources */, EC01FCC32B56650400BB2390 /* DetailsViewModel.swift in Sources */, ECB26A1B2B40746C00FE06B3 /* FriendsViewModel.swift in Sources */, diff --git a/Sources/Api/Sources/Api/Factory/FactoryApiBet.swift b/Sources/Api/Sources/Api/Factory/FactoryApiBet.swift index 2b45c9d..2465156 100644 --- a/Sources/Api/Sources/Api/Factory/FactoryApiBet.swift +++ b/Sources/Api/Sources/Api/Factory/FactoryApiBet.swift @@ -31,63 +31,26 @@ public class FactoryApiBet: FactoryBet { return json } - public func fromJsonDateString(_ dateString: String) -> Date? { - let formatter = DateFormatter() - formatter.locale = Locale(identifier: "en_US_POSIX") - formatter.dateFormat = "yyyy-MM-dd HH:mm:ss Z" - let date = formatter.date(from: dateString) - return date - } - public func toBet(from json: [String: Any]) -> Bet? { - guard let id = json["id"] as? String, - let theme = json["theme"] as? String, - let phrase = json["sentenceBet"] as? String, - let endRegisterDateString = json["endRegistration"] as? String, - let endBetDateString = json["endBet"] as? String, - let isPublic = json["isPrivate"] as? Bool, - let createdBy = json["createdBy"] as? String, - let type = json["type"] as? String, - let status = json["status"] as? String else { + guard let type = json["type"] as? String, let jsonData = try? JSONSerialization.data(withJSONObject: json, options: []) else { return nil } - - guard let endRegisterDate = fromJsonDateString(endRegisterDateString), - let endBetDate = fromJsonDateString(endBetDateString) else { + let decoder = JSONDecoder() + do { + switch type { + case "BINARY": + return try decoder.decode(BinaryBet.self, from: jsonData) + case "MATCH": + return try decoder.decode(MatchBet.self, from: jsonData) + case "CUSTOM": + return try decoder.decode(CustomBet.self, from: jsonData) + default: + return try decoder.decode(BinaryBet.self, from: jsonData) + } + } catch { + print("Error decoding Bet object: \(error)") return nil } - - return toBet(id: id, theme: theme, description: phrase, endRegister: endRegisterDate, endBet: endBetDate, isPublic: isPublic, status: toStatus(status: status), creator: User(username: createdBy, email: createdBy, nbCoins: 0, friends: []), type: type) - } - - func toStatus(status: String) -> BetStatus { - switch status { - case "IN_PROGRESS": - return .inProgress - case "WAITING": - return .waiting - case "CLOSING": - return .closing - case "FINISHED": - return .finished - case "CANCELLED": - return .cancelled - default: - return .finished - } - } - - public func toBet(id: String, theme: String, description: String, endRegister: Date, endBet: Date, isPublic: Bool, status: BetStatus, creator: User, type: String) -> Bet { - switch type { - case "BINARY": - return BinaryBet(id: id, theme: theme, phrase: description, endRegisterDate: endRegister, endBetDate: endBet, isPublic: isPublic, status: status, invited: [], author: creator, registered: []) - case "MATCH": - return MatchBet(id: id, theme: theme, phrase: description, endRegisterDate: endRegister, endBetDate: endBet, isPublic: isPublic, status: status, invited: [], author: creator, registered: [], nameTeam1: "", nameTeam2: "") - case "CUSTOM": - return CustomBet(id: id, theme: theme, phrase: description, endRegisterDate: endRegister, endBetDate: endBet, isPublic: isPublic, status: status, invited: [], author: creator, registered: []) - default: - return BinaryBet(id: id, theme: theme, phrase: description, endRegisterDate: endRegister, endBetDate: endBet, isPublic: isPublic, status: status, invited: [], author: creator, registered: []) - } } public func toBetDetail(from json: [String: Any]) -> BetDetail? { @@ -98,51 +61,27 @@ public class FactoryApiBet: FactoryBet { var participations: [Participation] = [] - if let participationsJson = json["participations"] as? [[String: Any]], !participationsJson.isEmpty { - for participationJson in participationsJson { - if let participation = self.toParticipate(from: participationJson) { - participations.append(participation) - } + if let participationsJson = json["participations"] as? [[String: Any]] { + do { + participations = try JSONDecoder().decode([Participation].self, from: JSONSerialization.data(withJSONObject: participationsJson)) + } catch { + print("Error decoding participations: \(error)") } } var answers: [AnswerDetail] = [] - if let answersJson = json["answers"] as? [[String: Any]], !answersJson.isEmpty { - for answer in answersJson { - if let answer = self.toAnswer(from: answer) { - answers.append(answer) - } + if let answersJson = json["answers"] as? [[String: Any]] { + do { + answers = try JSONDecoder().decode([AnswerDetail].self, from: JSONSerialization.data(withJSONObject: answersJson)) + } catch { + print("Error decoding answers: \(error)") } } return BetDetail(bet: bet, answers: answers, participations: participations) } - public func toParticipate(from json: [String: Any]) -> Participation? { - guard let id = json["id"] as? String, - let betId = json["betId"] as? String, - let username = json["username"] as? String, - let answer = json["answer"] as? String, - let stake = json["stake"] as? Int else { - return nil - } - - return Participation(id: id, stake: stake, date: Date(), response: answer, user: User(username: username, email: "Email", nbCoins: 0, friends: []), betId: betId) - } - - public func toAnswer(from json: [String: Any]) -> AnswerDetail? { - guard let response = json["response"] as? String, - let totalStakes = json["totalStakes"] as? Int, - let totalParticipants = json["totalParticipants"] as? Int, - let highestStake = json["highestStake"] as? Int, - let odds = json["odds"] as? Float else { - return nil - } - - return AnswerDetail(response: response, totalStakes: totalStakes, totalParticipants: totalParticipants, highestStake: highestStake, odds: odds) - } - public func betTypeString(fromType type: String) -> String { switch type { case "BinaryBet": diff --git a/Sources/Model/Sources/Model/AnswerDetail.swift b/Sources/Model/Sources/Model/AnswerDetail.swift index 9b87ed4..fb0baef 100644 --- a/Sources/Model/Sources/Model/AnswerDetail.swift +++ b/Sources/Model/Sources/Model/AnswerDetail.swift @@ -8,8 +8,8 @@ import Foundation /// A class representing detailed information about a specific answer option for a bet. -public class AnswerDetail: ObservableObject, Hashable { - +public class AnswerDetail: ObservableObject, Identifiable, Codable { + /// The response or outcome associated with this answer. public private(set) var response: String @@ -21,7 +21,7 @@ public class AnswerDetail: ObservableObject, Hashable { /// The highest stake placed on this answer. public private(set) var highestStake: Int - + /// The odds associated with this answer. public private(set) var odds: Float @@ -41,16 +41,4 @@ public class AnswerDetail: ObservableObject, Hashable { self.odds = odds } - public func hash(into hasher: inout Hasher) { - // Use properties that define the uniqueness of the instance for hashing - hasher.combine(response) - // ... (combine other properties) - } - - public static func == (lhs: AnswerDetail, rhs: AnswerDetail) -> Bool { - // Check equality based on properties - return lhs.response == rhs.response - // ... (check other properties) - } - } diff --git a/Sources/Model/Sources/Model/Bet.swift b/Sources/Model/Sources/Model/Bet.swift index 861c6df..a976803 100644 --- a/Sources/Model/Sources/Model/Bet.swift +++ b/Sources/Model/Sources/Model/Bet.swift @@ -8,7 +8,7 @@ import Foundation /// A class representing a betting entity, including details about the bet theme, participants, and deadlines. -public class Bet: ObservableObject, Identifiable { +public class Bet: ObservableObject, Identifiable, Codable { /// The id for the bet. public private(set) var id: String @@ -32,15 +32,42 @@ public class Bet: ObservableObject, Identifiable { public private(set) var status: BetStatus /// List of users who are invited to participate in the bet. - public private(set) var invited: [User] + public private(set) var invited: [User] = [] /// The user who created the bet. - public private(set) var author: User + public private(set) var author: String /// List of users who have registered for the bet. - public private(set) var registered: [User] + public private(set) var registered: [User] = [] + + private enum CodingKeys: String, CodingKey { + case id + case theme + case phrase = "sentenceBet" + case endRegisterDate = "endRegistration" + case endBetDate = "endBet" + case isPublic = "isPrivate" + case status + case author = "createdBy" + } + + public required init(from decoder: Decoder) throws { + let formatter = DateFormatter() + formatter.locale = Locale(identifier: "en_US_POSIX") + formatter.dateFormat = "yyyy-MM-dd HH:mm:ss Z" + let container = try decoder.container(keyedBy: CodingKeys.self) + self.id = try container.decode(String.self, forKey: .id) + self.theme = try container.decode(String.self, forKey: .theme) + self.phrase = try container.decode(String.self, forKey: .phrase) + let endRegisterDateString = try container.decode(String.self, forKey: .endRegisterDate) + self.endRegisterDate = formatter.date(from: endRegisterDateString)! + let endBetDateString = try container.decode(String.self, forKey: .endBetDate) + self.endBetDate = formatter.date(from: endBetDateString)! + self.isPublic = try container.decode(Bool.self, forKey: .isPublic) + self.status = try container.decode(BetStatus.self, forKey: .status) + self.author = try container.decode(String.self, forKey: .author) + } - /// Custom Constructor /// /// - Parameters: @@ -54,7 +81,7 @@ public class Bet: ObservableObject, Identifiable { /// - invited: List of users who are invited to participate in the bet. /// - author: The user who created the bet. /// - registered: List of users who have registered for the bet. - public init(id: String, theme: String, phrase: String, endRegisterDate: Date, endBetDate: Date, isPublic: Bool, status: BetStatus, invited: [User], author: User, registered: [User]) { + public init(id: String, theme: String, phrase: String, endRegisterDate: Date, endBetDate: Date, isPublic: Bool, status: BetStatus, invited: [User], author: String, registered: [User]) { self.id = id self.theme = theme self.phrase = phrase @@ -79,7 +106,7 @@ public class Bet: ObservableObject, Identifiable { /// - invited: List of users who are invited to participate in the bet. /// - author: The user who created the bet. /// - registered: List of users who have registered for the bet. - public init(theme: String, phrase: String, endRegisterDate: Date, endBetDate: Date, isPublic: Bool, status: BetStatus, invited: [User], author: User, registered: [User]) { + public init(theme: String, phrase: String, endRegisterDate: Date, endBetDate: Date, isPublic: Bool, status: BetStatus, invited: [User], author: String, registered: [User]) { self.id = UUID().uuidString self.theme = theme self.phrase = phrase @@ -92,14 +119,4 @@ public class Bet: ObservableObject, Identifiable { self.registered = registered } - /// Adds a new user to the list of registered participants for the bet. - /// - /// - Parameter newUser: The user to be added to the list of registered participants. - public func addRegistered(newUser: User){ - self.registered.append(newUser) - } - - public func isFinish() -> Bool{ - self.endBetDate < Date() - } } diff --git a/Sources/Model/Sources/Model/BetDetail.swift b/Sources/Model/Sources/Model/BetDetail.swift index 1d172c4..8f139f5 100644 --- a/Sources/Model/Sources/Model/BetDetail.swift +++ b/Sources/Model/Sources/Model/BetDetail.swift @@ -46,13 +46,7 @@ public class BetDetail: ObservableObject { /// Adds a new user participation to the list of participations for the bet. /// /// - Parameter newParticipation: The new user participation to be added. - public func addParticipation(newParticipation: Participation){ - - if !self.bet.registered.contains(where: { existingUser in - return existingUser.email == newParticipation.user.email - }) { - self.bet.addRegistered(newUser: newParticipation.user) - } + public func addParticipation(newParticipation: Participation) { self.participations.append(newParticipation) } } diff --git a/Sources/Model/Sources/Model/Enums/BetStatus.swift b/Sources/Model/Sources/Model/Enums/BetStatus.swift index 3c32829..26eb874 100644 --- a/Sources/Model/Sources/Model/Enums/BetStatus.swift +++ b/Sources/Model/Sources/Model/Enums/BetStatus.swift @@ -7,6 +7,11 @@ import Foundation -public enum BetStatus { - case inProgress, waiting, closing, finished, cancelled +public enum BetStatus: String, Codable { + case inProgress = "IN_PROGRESS" + case waiting = "WAITING" + case closing = "CLOSING" + case finished = "FINISHED" + case cancelled = "CANCELLED" } + diff --git a/Sources/Model/Sources/Model/Factory/FactoryBet.swift b/Sources/Model/Sources/Model/Factory/FactoryBet.swift index d4c65a4..368cd19 100644 --- a/Sources/Model/Sources/Model/Factory/FactoryBet.swift +++ b/Sources/Model/Sources/Model/Factory/FactoryBet.swift @@ -11,5 +11,4 @@ public protocol FactoryBet { func toResponse(bet: Bet) -> [String: Any] func toBet(from json: [String: Any]) -> Bet? func toBetDetail(from json: [String: Any]) -> BetDetail? - func toBet(id: String, theme: String, description: String, endRegister: Date, endBet: Date, isPublic: Bool, status: BetStatus, creator: User, type: String) -> Bet } diff --git a/Sources/Model/Sources/Model/MatchBet.swift b/Sources/Model/Sources/Model/MatchBet.swift index 0955e8b..8bea44a 100644 --- a/Sources/Model/Sources/Model/MatchBet.swift +++ b/Sources/Model/Sources/Model/MatchBet.swift @@ -15,6 +15,11 @@ public class MatchBet: Bet { /// The name of the second team involved in the match. public var nameTeam2: String + private enum CodingKeys: String, CodingKey { + case nameTeam1 + case nameTeam2 + } + /// Custom Constructor /// /// - Parameters: @@ -30,7 +35,7 @@ public class MatchBet: Bet { /// - registered: List of users who have registered for the match bet. /// - nameTeam1: The name of the first team involved in the match. /// - nameTeam2: The name of the second team involved in the match. - public init(id: String, theme: String, phrase: String, endRegisterDate: Date, endBetDate: Date, isPublic: Bool, status: BetStatus, invited: [User], author: User, registered: [User], nameTeam1: String, nameTeam2: String) { + public init(id: String, theme: String, phrase: String, endRegisterDate: Date, endBetDate: Date, isPublic: Bool, status: BetStatus, invited: [User], author: String, registered: [User], nameTeam1: String, nameTeam2: String) { self.nameTeam1 = nameTeam1 self.nameTeam2 = nameTeam2 super.init(id: id, theme: theme, phrase: phrase, endRegisterDate: endRegisterDate, endBetDate: endBetDate, isPublic: isPublic, status: status, invited: invited, author: author, registered: registered) @@ -50,9 +55,24 @@ public class MatchBet: Bet { /// - registered: List of users who have registered for the match bet. /// - nameTeam1: The name of the first team involved in the match. /// - nameTeam2: The name of the second team involved in the match. - public init(theme: String, phrase: String, endRegisterDate: Date, endBetDate: Date, isPublic: Bool, status: BetStatus, invited: [User], author: User, registered: [User], nameTeam1: String, nameTeam2: String) { + public init(theme: String, phrase: String, endRegisterDate: Date, endBetDate: Date, isPublic: Bool, status: BetStatus, invited: [User], author: String, registered: [User], nameTeam1: String, nameTeam2: String) { self.nameTeam1 = nameTeam1 self.nameTeam2 = nameTeam2 super.init(theme: theme, phrase: phrase, endRegisterDate: endRegisterDate, endBetDate: endBetDate, isPublic: isPublic, status: status, invited: invited, author: author, registered: registered) } + + public required init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: CodingKeys.self) + self.nameTeam1 = try container.decode(String.self, forKey: .nameTeam1) + self.nameTeam2 = try container.decode(String.self, forKey: .nameTeam2) + try super.init(from: decoder) + } + + public override func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encode(nameTeam1, forKey: .nameTeam1) + try container.encode(nameTeam2, forKey: .nameTeam2) + try super.encode(to: encoder) + } + } diff --git a/Sources/Model/Sources/Model/Participation.swift b/Sources/Model/Sources/Model/Participation.swift index c435ff9..60d65e4 100644 --- a/Sources/Model/Sources/Model/Participation.swift +++ b/Sources/Model/Sources/Model/Participation.swift @@ -8,20 +8,18 @@ import Foundation /// A class representing a user's participation in a bet. -public class Participation: ObservableObject, Identifiable { +public class Participation: ObservableObject, Codable, Identifiable { public let id: String + /// The amount of stake in the bet. public private(set) var stake: Int - /// The date and time when the participation occurred. - public private(set) var date: Date - /// The response or outcome of the participation. - public private(set) var response: String + public private(set) var answer: String /// The user who participated in the bet. - public private(set) var user: User + public private(set) var username: String /// The unique identifier of the bet. public private(set) var betId: String @@ -34,12 +32,11 @@ public class Participation: ObservableObject, Identifiable { /// - response: The response or outcome of the participation. /// - user: The user who participated in the bet. /// - betId: The unique identifier of the bet. - public init(id: String, stake: Int, date: Date, response: String, user: User, betId: String) { + public init(id: String, stake: Int, answer: String, username: String, betId: String) { self.id = id self.stake = stake - self.date = date - self.response = response - self.user = user + self.answer = answer + self.username = username self.betId = betId } } diff --git a/Sources/Model/Sources/Model/User.swift b/Sources/Model/Sources/Model/User.swift index f9fb2c1..9bb5e79 100644 --- a/Sources/Model/Sources/Model/User.swift +++ b/Sources/Model/Sources/Model/User.swift @@ -8,7 +8,7 @@ import Foundation /// A struct representing a user with details such as username, email, number of coins, and friends. -public struct User { +public struct User: Codable { /// The username of the user. public private(set) var username: String @@ -19,41 +19,15 @@ public struct User { /// The number of coins associated with the user. public var nbCoins: Int - /// List of friends associated with the user. - public private(set) var friends: [User] - /// Custom constructor to initialize a User instance. /// /// - Parameters: /// - username: The username of the user. /// - email: The email address of the user. /// - nbCoins: The number of coins associated with the user. - /// - friends: List of friends associated with the user. - public init(username: String, email: String, nbCoins: Int, friends: [User]) { + public init(username: String, email: String, nbCoins: Int) { self.username = username self.email = email self.nbCoins = nbCoins - self.friends = friends - } - - /// Adds a new friend to the list of friends for the user. - /// - /// - Parameter user: The user to be added as a friend. - public mutating func addFriend(user: User) { - self.friends.append(user) - } - - /// Maps user data from a JSON dictionary to create a User instance. - /// - /// - Parameter json: The JSON dictionary containing user data. - /// - Returns: An optional User instance if mapping is successful, otherwise nil. - public static func mapUser(from json: [String: Any]) -> User? { - guard let username = json["username"] as? String, - let email = json["email"] as? String, - let nbCoins = json["nbCoins"] as? Int else { - return nil - } - - return User(username: username, email: email, nbCoins: nbCoins, friends: []) } } diff --git a/Sources/StubLib/Sources/StubLib/Stub.swift b/Sources/StubLib/Sources/StubLib/Stub.swift index 55bba55..a6e15cd 100644 --- a/Sources/StubLib/Sources/StubLib/Stub.swift +++ b/Sources/StubLib/Sources/StubLib/Stub.swift @@ -21,16 +21,14 @@ struct Stub { public mutating func loadBets() { - var user1 = User(username: "Lucas", email: "lucas.delanier@etu.uca.fr", nbCoins: 100, friends: []) + let user1 = User(username: "Lucas", email: "lucas.delanier@etu.uca.fr", nbCoins: 100) users.append(user1) - var user2 = User(username: "Imri", email: "emre.kartal@etu.uca.fr", nbCoins: 75, friends: [user1]) + let user2 = User(username: "Imri", email: "emre.kartal@etu.uca.fr", nbCoins: 75) users.append(user2) - user1.addFriend(user: user2) - let user3 = User(username: "Arthur", email: "arthur.valin@etu.uca.fr", nbCoins: 30, friends: [user2]) + let user3 = User(username: "Arthur", email: "arthur.valin@etu.uca.fr", nbCoins: 30) users.append(user3) - user2.addFriend(user: user3) let bet1 = BinaryBet( theme: "Football - Finale de la Ligue des Champions", @@ -40,7 +38,7 @@ struct Stub { isPublic: true, status: .inProgress, invited: [], - author: user1, + author: "Lucas", registered: [user2] ) self.bets.append(bet1) @@ -53,7 +51,7 @@ struct Stub { isPublic: false, status: .inProgress, invited: [user3], - author: user1, + author: "Lucas", registered: [user2] ) self.bets.append(bet2) @@ -66,7 +64,7 @@ struct Stub { isPublic: true, status: .finished, invited: [], - author: user1, + author: "Lucas", registered: [user2, user1, user3] ) self.bets.append(bet3) @@ -79,7 +77,7 @@ struct Stub { isPublic: false, status: .finished, invited: [user1], - author: user2, + author: "Lucase", registered: [user3] ) self.bets.append(bet4) @@ -89,10 +87,10 @@ struct Stub { self.betsDetail.append(betDetail) } - self.betsDetail[0].addParticipation(newParticipation: Participation(id: UUID().uuidString, stake: 120, date: Date(), response: "OUI", user: user1, betId: "1")) - self.betsDetail[0].addParticipation(newParticipation: Participation(id: UUID().uuidString, stake: 20, date: Date(), response: "NON", user: user2, betId: "2")) - self.betsDetail[0].addParticipation(newParticipation: Participation(id: UUID().uuidString, stake: 320, date: Date(), response: "OUI", user: user3, betId: "3")) - self.betsDetail[0].addParticipation(newParticipation: Participation(id: UUID().uuidString, stake: 320, date: Date(), response: "OUI", user: user3, betId: "3")) + self.betsDetail[0].addParticipation(newParticipation: Participation(id: UUID().uuidString, stake: 120, answer: "OUI", username: user1.username, betId: "1")) + self.betsDetail[0].addParticipation(newParticipation: Participation(id: UUID().uuidString, stake: 20, answer: "NON", username: user2.username, betId: "2")) + self.betsDetail[0].addParticipation(newParticipation: Participation(id: UUID().uuidString, stake: 320, answer: "OUI", username: user3.username, betId: "3")) + self.betsDetail[0].addParticipation(newParticipation: Participation(id: UUID().uuidString, stake: 320, answer: "OUI", username: user3.username, betId: "3")) }