From 473b4b5bfb0ca7d6b9a8513ebbee16e5b0302a61 Mon Sep 17 00:00:00 2001 From: Eric Wang Date: Sun, 26 Nov 2023 23:06:14 -0500 Subject: [PATCH 1/2] some comments --- app/newHere1/.DS_Store | Bin 6148 -> 6148 bytes .../newHere.xcodeproj/project.pbxproj | 4 +- app/newHere1/newHere/ContentView.swift | 19 +- .../newHere/CustomARViewRepresentable.swift | 6 +- app/newHere1/newHere/Home.swift | 253 +++++------------- app/newHere1/newHere/LoginView.swift | 18 +- app/newHere1/newHere/Message.swift | 5 +- app/newHere1/newHere/Post.swift | 3 +- app/newHere1/newHere/Registration.swift | 3 +- app/newHere1/newHere/api_call.swift | 10 +- 10 files changed, 108 insertions(+), 213 deletions(-) diff --git a/app/newHere1/.DS_Store b/app/newHere1/.DS_Store index 6297406ba78c9f8103cb80ad99f2d4b1808d1755..71d761b2d2791935e8e298d4fac77ff03af26523 100644 GIT binary patch delta 55 zcmZoMXffDe%*@PiXzSzxW{t^l%v@}Oy(!*OU6b>eO*kyXg${~$9d(? Date: Sun, 26 Nov 2023 23:52:33 -0500 Subject: [PATCH 2/2] documentation --- app/newHere1/newHere/CustomARView.swift | 7 ++ .../newHere/CustomARViewRepresentable.swift | 27 ++++--- app/newHere1/newHere/LoginView.swift | 38 ++++++--- app/newHere1/newHere/Messages.swift | 15 +++- app/newHere1/newHere/Profile.swift | 28 ++++--- app/newHere1/newHere/Registration.swift | 49 +++++------ app/newHere1/newHere/api_call.swift | 81 ++++++++++++++----- 7 files changed, 166 insertions(+), 79 deletions(-) diff --git a/app/newHere1/newHere/CustomARView.swift b/app/newHere1/newHere/CustomARView.swift index 380db0e..e162fd5 100644 --- a/app/newHere1/newHere/CustomARView.swift +++ b/app/newHere1/newHere/CustomARView.swift @@ -4,20 +4,27 @@ // // Created by TRACY LI on 2023/10/28. // +// Description: +// This file defines a CustomARView class, which is a subclass of ARView from RealityKit. +// It's designed to be used for augmented reality experiences within the 'new_here' application. import ARKit import RealityKit import SwiftUI +/// CustomARView is a subclass of ARView for creating custom AR experiences. class CustomARView: ARView { + /// Initializes and returns a newly allocated view object with the specified frame rectangle. required init (frame frameRect: CGRect){ super.init(frame: frameRect) } + /// Returns an object initialized from data in a given unarchiver. dynamic required init?(coder decoder: NSCoder){ fatalError("init(coder:) has not been implemented") } + /// Convenience initializer to create a view with the main screen bounds. convenience init(){ self.init(frame: UIScreen.main.bounds) } diff --git a/app/newHere1/newHere/CustomARViewRepresentable.swift b/app/newHere1/newHere/CustomARViewRepresentable.swift index 1b6b7e5..41a44ba 100644 --- a/app/newHere1/newHere/CustomARViewRepresentable.swift +++ b/app/newHere1/newHere/CustomARViewRepresentable.swift @@ -4,31 +4,32 @@ // // Created by TRACY LI on 2023/10/28. // +// Description: +// This Swift file defines a SwiftUI UIViewRepresentable for integrating ARKit functionality. +// It allows for rendering AR content, such as 3D bubbles with messages, in the 'new_here' application. +// import SwiftUI import ARKit -//class ARViewModel: ObservableObject { -// var messageToPlant: Message? -// -// func plantMessage(_ message: Message) { -// messageToPlant = message -// } -//} - struct CustomARViewRepresentable: UIViewRepresentable { + // Binding for user ID @Binding var userId: String + + // Environment objects for message and fetched messages states @EnvironmentObject var messageState: MessageState @EnvironmentObject var fetchedMessagesState: FetchedMessagesState + // Create the ARSCNView and configure it func makeUIView(context: Context) -> ARSCNView { -// return CustomARView() let sceneView = ARSCNView() sceneView.delegate = context.coordinator let configuration = ARWorldTrackingConfiguration() sceneView.session.run(configuration) print("user id: \(userId)") + + // Fetch and render user's messages getUserMessages(userId: userId) { result in switch result { @@ -60,6 +61,7 @@ struct CustomARViewRepresentable: UIViewRepresentable { return sceneView } + // Update the AR view when a new message is available func updateUIView (_ uiView: ARSCNView, context: Context) { if let messageToPlant = messageState.currentMessage { plantBubbleNode(to: uiView, message: messageToPlant) @@ -68,6 +70,7 @@ struct CustomARViewRepresentable: UIViewRepresentable { } + // Create a coordinator for handling ARSCNViewDelegate methods func makeCoordinator() -> Coordinator { Coordinator(self) } @@ -80,6 +83,7 @@ struct CustomARViewRepresentable: UIViewRepresentable { } } + // Plant a bubble node with a message at a specific position func plantBubbleNode(to sceneView: ARSCNView, message: Message) { // Set the position based on the provided position if let frame = sceneView.session.currentFrame { @@ -99,6 +103,7 @@ struct CustomARViewRepresentable: UIViewRepresentable { } } + // Render a history of bubble nodes with messages at random positions func renderBubbleNodeHistory(to sceneView: ARSCNView, messages: [Message]) { // Create a random number generator var randomNumberGenerator = SystemRandomNumberGenerator() @@ -121,6 +126,7 @@ struct CustomARViewRepresentable: UIViewRepresentable { } } + // Create a new bubble node with a message at a specified position func newBubbleNode(to sceneView: ARSCNView, message:Message, position:SCNVector3) { let bubble = SCNSphere(radius: 0.05) bubble.firstMaterial?.diffuse.contents = UIColor(red: 135.0/255.0, green: 206.0/255.0, blue: 235.0/255.0, alpha: 1.0) @@ -132,9 +138,6 @@ struct CustomARViewRepresentable: UIViewRepresentable { textGeometry1.firstMaterial?.diffuse.contents = UIColor.black let textNode1 = SCNNode(geometry: textGeometry1) - - // TEXT POSITIONING RELATIVE TO BUBBLE - WILL HAVE TO ADJUST LATER - // Position the first textNode inside the bubble textNode1.position = SCNVector3(-0.05, 0, -0.05) // Adjust the position inside the bubble textNode1.scale = SCNVector3(0.001, 0.001, 0.001) diff --git a/app/newHere1/newHere/LoginView.swift b/app/newHere1/newHere/LoginView.swift index 6507212..91c9ae9 100644 --- a/app/newHere1/newHere/LoginView.swift +++ b/app/newHere1/newHere/LoginView.swift @@ -4,19 +4,28 @@ // // Created by TRACY LI on 2023/11/7. // +// Description: +// This Swift file defines the LoginView, which is responsible for user login in the 'newHere' application. +// It includes input fields for username and password, and login logic to communicate with a remote server. +// import SwiftUI +// Define the URL and API Key for the login API let loginUrlString = "https://here-swe.vercel.app/auth/login" let logApiKey = "qe5YT6jOgiA422_UcdbmVxxG1Z6G48aHV7fSV4TbAPs" struct LoginView: View { + // User input fields @State internal var username: String = "" @State internal var password: String = "" + + // State variables @State internal var isRegistered = false @Binding var isAuthenticated: Bool @Binding var user_id: String + // Alert properties to display login status @State internal var showingAlert = false @State internal var alertMessage = "" @@ -28,18 +37,21 @@ struct LoginView: View { .font(.largeTitle) .padding(.bottom, 50) + // Username input field TextField("Username", text: $username) .padding() .background(Color(.systemGray6)) .cornerRadius(5.0) .padding(.bottom, 20) + // Password input field SecureField("Password", text: $password) .padding() .background(Color(.systemGray6)) .cornerRadius(5.0) .padding(.bottom, 20) + // Login button Button(action: LogInUser) { Text("Login") .frame(minWidth: 0, maxWidth: .infinity) @@ -51,6 +63,7 @@ struct LoginView: View { } .padding(.horizontal) + // Navigation link to registration view NavigationLink(destination: RegistrationView(isRegistered: $isRegistered, userId: $user_id)) { Text("Don't have an account? Signup") } @@ -64,7 +77,8 @@ struct LoginView: View { } } } - + + // Function to handle user login func LogInUser(){ if username.isEmpty || password.isEmpty { self.alertMessage = "Please enter both username and password." @@ -73,28 +87,35 @@ struct LoginView: View { } print("Login user called") + + // Prepare the request body with user input let requestBody: [String: Any] = [ "inputLogin": username, "password": password] + // Convert the request body to JSON data guard let jsonData = try? JSONSerialization.data(withJSONObject: requestBody) else{ return } + // Create the URL for the login API guard let url = URL(string: loginUrlString) else { return } + // Create a URLRequest for the API request var request = URLRequest(url: url) request.httpMethod = "POST" request.httpBody = jsonData request.setValue("application/json", forHTTPHeaderField: "Content-Type") request.setValue(logApiKey, forHTTPHeaderField: "x-api-key") - + + // Perform the network request URLSession.shared.dataTask(with: request) { data, response, error in DispatchQueue.main.async { if let error = error{ + // Handle network error self.alertMessage = "Login failed: \(error.localizedDescription)" self.showingAlert = true }else if let httpResponse = response as? HTTPURLResponse{ @@ -109,6 +130,7 @@ struct LoginView: View { if let json = try JSONSerialization.jsonObject(with: jsonData, options:[]) as? [String: Any], let extractedUserId = json["_id"] as? String { print("User id: \(extractedUserId)") + // Extract and store user ID self.user_id = extractedUserId; print("updated: \(self.user_id)") UserDefaults.standard.set(extractedUserId, forKey: "UserId") @@ -118,6 +140,7 @@ struct LoginView: View { if let json = try JSONSerialization.jsonObject(with: jsonData, options:[]) as? [String: Any], let userName = json["userName"] as? String { print("User Name:\(userName)") + // Store user's username UserDefaults.standard.set(userName, forKey: "UserName") } } catch { @@ -125,19 +148,22 @@ struct LoginView: View { } } } + // Set the user as authenticated self.isAuthenticated = true; print("authenticated: \(self.isAuthenticated)") print("user_id: \(self.user_id)") } }else if statusCode == 404 { + // Handle user not found self.alertMessage = "User not found. Please check your credentials." self.showingAlert = true }else{ + // Handle other server errors self.alertMessage = "Login failed: Server returned status code \(httpResponse.statusCode)" self.showingAlert = true } }else{ - // General error + // Handle unexpected errors self.alertMessage = "Login failed: Unexpected error occurred" self.showingAlert = true } @@ -148,9 +174,3 @@ struct LoginView: View { } } -//// Preview Provider -//struct LoginView_Previews: PreviewProvider { -// static var previews: some View { -// LoginView(isAuthenticated: $isAuthenticated) -// } -//} diff --git a/app/newHere1/newHere/Messages.swift b/app/newHere1/newHere/Messages.swift index 8aede6e..0655090 100644 --- a/app/newHere1/newHere/Messages.swift +++ b/app/newHere1/newHere/Messages.swift @@ -4,19 +4,26 @@ // // Created by Eric Wang on 10/14/23. // +// Description: +// This file defines the MessagesPopup view, which is used to display a popup of messages +// within the 'here' application. It includes a background image, a list of messages, and +// a close button to dismiss the view. import SwiftUI +/// View for displaying a popup with messages. struct MessagesPopup: View { - @Binding var isPresented: Bool - + @Binding var isPresented: Bool // Binding to control the visibility of the popup + var body: some View { ZStack{ + // Background image for the popup Image("cross_campus") .font(.system(size: 50)) LazyVStack { HStack { Spacer() + // Close button for the popup Button(action: { isPresented.toggle() // Close the popup }) { @@ -27,8 +34,9 @@ struct MessagesPopup: View { .foregroundColor(.white) .cornerRadius(10) } - .padding(.trailing, 20) // Adjust the position of the close button + .padding(.trailing, 20) } + // Loop to create message buttons ForEach(1...5, id: \.self) { count in Button(action: {}) { HStack { @@ -50,6 +58,7 @@ struct MessagesPopup: View { } } +/// Subview for displaying a profile picture. struct ProfilePicture: View { var body : some View { Image("profilePicture") diff --git a/app/newHere1/newHere/Profile.swift b/app/newHere1/newHere/Profile.swift index 5d567ad..ca536eb 100644 --- a/app/newHere1/newHere/Profile.swift +++ b/app/newHere1/newHere/Profile.swift @@ -1,15 +1,16 @@ - // // Profile.swift // here // // Created by Lindsay Chen on 10/13/23. // +// Description: +// This file defines the ProfilePopup view along with its subviews such as ProfileHeader, ProfileButtons, +// ProfileStats, and PostGrid for the 'here' application. It includes functionality for displaying user profiles +// with options to view and edit profile details. import SwiftUI -let apiString = "https://here-swe.vercel.app/auth/user" -let apiKey = "qe5YT6jOgiA422_UcdbmVxxG1Z6G48aHV7fSV4TbAPs" let userId = UserDefaults.standard.string(forKey: "UserId") ?? "" let userName = UserDefaults.standard.string(forKey: "UserName") ?? "" @@ -22,6 +23,7 @@ struct ProfilePopup: View { ZStack { Color.white VStack { + // Header with close button HStack { Spacer() Button(action: { @@ -36,9 +38,9 @@ struct ProfilePopup: View { } .padding(.trailing, 20) // Adjust the position of the close button } - ProfileHeader() + ProfileHeader() // User profile header Divider() - ProfileStats() + ProfileStats() // User profile statistics Divider() Button(action: { isShowingFriends.toggle() @@ -49,7 +51,7 @@ struct ProfilePopup: View { .border(Color.gray, width: 1) .cornerRadius(5) } - PostGrid() + PostGrid() // Grid to show posts } .sheet(isPresented: $isShowingFriends) { // Friends.swift => Friends struct @@ -60,16 +62,18 @@ struct ProfilePopup: View { } } +/// View for displaying the user's profile header. struct ProfileHeader: View { var body: some View { HStack { + // Profile picture Image("profilePicture") .resizable() .aspectRatio(contentMode: .fill) .frame(width: 80, height: 80) .clipShape(Circle()) .padding() - + // User name and bio VStack(alignment: .leading, spacing: 8) { Text(userName) @@ -87,10 +91,11 @@ struct ProfileHeader: View { } } +/// View for displaying profile action buttons. struct ProfileButtons: View { var body: some View { HStack { - + // Edit profile button Button(action: { // Action for Edit Profile }) { @@ -101,7 +106,7 @@ struct ProfileButtons: View { .cornerRadius(5) } - + // Add friend button Button(action: { // Action for Add Friend }) { @@ -116,6 +121,7 @@ struct ProfileButtons: View { } } +/// View for displaying profile statistics. struct ProfileStats: View { let stats: [(title: String, value: String)] = [ ("Notes", "10"), @@ -143,6 +149,7 @@ struct ProfileStats: View { } } +/// View for displaying individual profile stat item. struct ProfileStatItem: View { let title: String let value: String @@ -160,9 +167,10 @@ struct ProfileStatItem: View { } } +/// View for displaying the posts grid. struct PostGrid: View { var body: some View { - // Replace with a grid or list of posts + // Placeholder for posts grid Text("Posts Grid") .font(.title) .padding() diff --git a/app/newHere1/newHere/Registration.swift b/app/newHere1/newHere/Registration.swift index 0e750ab..8f254d4 100644 --- a/app/newHere1/newHere/Registration.swift +++ b/app/newHere1/newHere/Registration.swift @@ -4,12 +4,17 @@ // // Created by TRACY LI on 2023/11/4. // +// Description: +// This file defines the RegistrationView, which is used for user registration in the 'newHere' application. +// It includes form inputs for user details and a registration logic to communicate with a remote server. import SwiftUI +// URL and API Key for registration API let registerUrlString = "https://here-swe.vercel.app/auth/register" let regApiKey = "qe5YT6jOgiA422_UcdbmVxxG1Z6G48aHV7fSV4TbAPs" +/// View for user registration. struct RegistrationView: View { @State private var firstName: String = "" @State private var lastName: String = "" @@ -27,6 +32,7 @@ struct RegistrationView: View { var body: some View { NavigationView { Form { + // User name input section Section(header: Text("Name")) { TextField("First Name", text: $firstName) .disableAutocorrection(true) @@ -34,6 +40,7 @@ struct RegistrationView: View { .disableAutocorrection(true) } + // User credentials input section Section(header: Text("Credentials")) { TextField("Username", text: $userName) .disableAutocorrection(true) @@ -46,6 +53,7 @@ struct RegistrationView: View { .disableAutocorrection(true) } + // Submit button and navigation to login view Section { Button(action: registerUser) { Text("Submit") @@ -62,10 +70,12 @@ struct RegistrationView: View { } } } - + + // Function to handle user registration func registerUser() { - // Implement registration logic + // Registration logic implementation print("User registration logic goes here.") + // Creating request body let requestBody: [String: Any] = [ "firstName": firstName, "lastName": lastName, @@ -73,12 +83,14 @@ struct RegistrationView: View { "email": email, "password": password ] - + + // Converting request body to JSON guard let jsonData = try? JSONSerialization.data(withJSONObject: requestBody) else { return } - + + // Creating URL and URLRequest guard let url = URL(string: registerUrlString) else { return } @@ -88,44 +100,27 @@ struct RegistrationView: View { request.httpBody = jsonData request.setValue("application/json", forHTTPHeaderField: "Content-Type") request.setValue(regApiKey, forHTTPHeaderField: "x-api-key") - + + // Performing the network request URLSession.shared.dataTask(with: request) { data, response, error in DispatchQueue.main.async { + // Handling the response if let error = error { self.alertMessage = "Registration failed: \(error.localizedDescription)" self.showingAlert = true } else if let httpResponse = response as? HTTPURLResponse, httpResponse.statusCode == 200 { - // Assuming status code 200 means success + // Success handling self.alertMessage = "Registration successful" self.showingAlert = true self.isRegistered = true } else { + // Failure handling self.alertMessage = "Failed to register. Please try again." self.showingAlert = true } } }.resume() - -// URLSession.shared.dataTask(with: request) { data, response, error in -// if let error = error{ -// print("error:\(error)") -// } -// if let response = response { -// print("response:\(response)") -// } -// if let data = data { -// if let responseString = String(data: data, encoding: .utf8) { -// print("Response: \(responseString)") -// } -// self.isRegistered = true -// } -// }.resume() - } } -//struct RegistrationView_Previews: PreviewProvider { -// static var previews: some View { -// RegistrationView() -// } -//} + diff --git a/app/newHere1/newHere/api_call.swift b/app/newHere1/newHere/api_call.swift index 4c08e65..4a1c691 100644 --- a/app/newHere1/newHere/api_call.swift +++ b/app/newHere1/newHere/api_call.swift @@ -4,10 +4,17 @@ // // Created by Phuc Duong on 11/5/23. // - +// Description: +// This file contains structures and functions for handling API calls in the 'newHere' application. +// It includes functionalities for posting messages, fetching user messages, managing friends, etc. import Foundation +// Constants for API interaction and user defaults +let apiString = "https://here-swe.vercel.app/auth/user" +let apiKey = "qe5YT6jOgiA422_UcdbmVxxG1Z6G48aHV7fSV4TbAPs" + +// Structure for request body of posting a message struct PostMessageRequest: Codable { let user_id: String let text: String @@ -15,6 +22,7 @@ struct PostMessageRequest: Codable { let location: GeoJSONPoint } +// Structure for response of a message struct MessageResponse: Codable { let _id: String let user_id: String @@ -23,19 +31,17 @@ struct MessageResponse: Codable { let location: GeoJSONPoint } +// Structure for response of adding a friend struct AddFriendResponse: Codable { let message: String } - +// Structure for request body of adding a friend struct FriendRequest: Codable { let friendName: String } - - -let apiKey2 = "qe5YT6jOgiA422_UcdbmVxxG1Z6G48aHV7fSV4TbAPs" - +// Structure for user message data struct UserMessage: Codable { struct Location: Codable { let type: String @@ -51,35 +57,44 @@ struct UserMessage: Codable { let replies: [String] } +// Function to fetch user messages func getUserMessages(userId: String, completion: @escaping (Result<[MessageResponse], Error>) -> Void) { + // API URL let urlString = "https://here-swe.vercel.app/user/\(userId)/messages" + // URL validation guard let url = URL(string: urlString) else { completion(.failure(URLError(.badURL))) return } + // Setup request var request = URLRequest(url: url) request.httpMethod = "GET" request.addValue("application/json", forHTTPHeaderField: "Accept") - request.addValue(apiKey2, forHTTPHeaderField: "X-API-Key") + request.addValue(apiKey, forHTTPHeaderField: "X-API-Key") + // URLSession data task let task = URLSession.shared.dataTask(with: request) { data, response, error in + // Error handling if let error = error { completion(.failure(error)) return } + // Response status code check guard let httpResponse = response as? HTTPURLResponse, httpResponse.statusCode == 200 else { completion(.failure(URLError(.badServerResponse))) return } + // Data validation guard let data = data else { completion(.failure(URLError(.cannotParseResponse))) return } + // JSON decoding do { let messages = try JSONDecoder().decode([MessageResponse].self, from: data) completion(.success(messages)) @@ -90,7 +105,7 @@ func getUserMessages(userId: String, completion: @escaping (Result<[MessageRespo completion(.failure(error)) } } - + // Start the task task.resume() } @@ -98,31 +113,37 @@ func getUserMessages(userId: String, completion: @escaping (Result<[MessageRespo func postMessage(user_id: String, text: String, visibility: String, locationDataManager: LocationDataManager, completion: @escaping (Result) -> Void) { // make sure you can get the current location if let currentLocation = locationDataManager.location { - + // API URL guard let url = URL(string: "https://here-swe.vercel.app/message/post_message") else { print("Invalid URL") return } + // Setup request var request = URLRequest(url: url) - request.httpMethod = "POST" request.addValue("application/json", forHTTPHeaderField: "Content-Type") - request.addValue(apiKey2, forHTTPHeaderField: "X-API-Key") + request.addValue(apiKey, forHTTPHeaderField: "X-API-Key") + // Encode request body let requestBody = PostMessageRequest(user_id: user_id, text: text, visibility: visibility, location: currentLocation.toGeoJSONPoint()) request.httpBody = try? JSONEncoder().encode(requestBody) + + // URLSession data task let task = URLSession.shared.dataTask(with: request) { data, response, error in + // Error handling if let error = error { completion(.failure(error)) return } + // Response status code check guard let httpResponse = response as? HTTPURLResponse, (200...299).contains(httpResponse.statusCode), let data = data else { // Handle the case when the response is not an HTTPURLResponse or the status code is not in the range. let statusCode = (response as? HTTPURLResponse)?.statusCode ?? -1 completion(.failure(NSError(domain: "", code: statusCode, userInfo: nil))) return } + // JSON decoding do { let messageResponse = try JSONDecoder().decode(MessageResponse.self, from: data) completion(.success(messageResponse)) @@ -130,43 +151,50 @@ func postMessage(user_id: String, text: String, visibility: String, locationData completion(.failure(error)) } } + // Start the task task.resume() } } - +// Function to get all friends of a user func getAllUserFriends(userId: String, completion: @escaping (Result<[String], Error>) -> Void) { + // API URL let urlString = "https://here-swe.vercel.app/user/\(userId)/friends" + // URL validation guard let url = URL(string: urlString) else { completion(.failure(URLError(.badURL))) return } + // Setup request var request = URLRequest(url: url) request.httpMethod = "GET" request.addValue("application/json", forHTTPHeaderField: "Accept") - request.addValue(apiKey2, forHTTPHeaderField: "X-API-Key") + request.addValue(apiKey, forHTTPHeaderField: "X-API-Key") + // URLSession data task let task = URLSession.shared.dataTask(with: request) { data, response, error in + // Error handling if let error = error { completion(.failure(error)) return } + // Response status code check guard let httpResponse = response as? HTTPURLResponse, httpResponse.statusCode == 200 else { completion(.failure(URLError(.badServerResponse))) return } + // Data validation guard let data = data else { completion(.failure(URLError(.cannotParseResponse))) return } + // JSON deserialization do { - // Deserialize the JSON response into an array of strings -// let jsonArray = try JSONSerialization.jsonObject(with: data, options: []) as? [String] print("Friends") print(data) let jsonArray = try JSONSerialization.jsonObject(with: data, options: []) as? [String: String] @@ -182,23 +210,29 @@ func getAllUserFriends(userId: String, completion: @escaping (Result<[String], E } } + // Start the task task.resume() } +// Function to add a friend by name func addFriendByName(userId: String, friendName: String, completion: @escaping (Result) -> Void) { + // API URL let urlString = "https://here-swe.vercel.app/user/\(userId)/friends_name" + // URL validation guard let url = URL(string: urlString) else { completion(.failure(URLError(.badURL))) return } + // Setup request var request = URLRequest(url: url) request.httpMethod = "PUT" request.addValue("application/json", forHTTPHeaderField: "Content-Type") // Ensure this matches what's used in Postman request.addValue("application/json", forHTTPHeaderField: "Accept") - request.addValue(apiKey2, forHTTPHeaderField: "X-API-Key") + request.addValue(apiKey, forHTTPHeaderField: "X-API-Key") + // Encode request body let requestBody = FriendRequest(friendName: friendName) do { request.httpBody = try JSONEncoder().encode(requestBody) @@ -207,22 +241,27 @@ func addFriendByName(userId: String, friendName: String, completion: @escaping ( return } + // URLSession data task let task = URLSession.shared.dataTask(with: request) { data, response, error in + // Error handling if let error = error { completion(.failure(error)) return } + // Response status code check guard let httpResponse = response as? HTTPURLResponse else { completion(.failure(URLError(.badServerResponse))) return } + // Data validation guard let data = data else { completion(.failure(URLError(.cannotParseResponse))) return } + // JSON decoding do { let addFriendResponse = try JSONDecoder().decode(AddFriendResponse.self, from: data) completion(.success(addFriendResponse)) @@ -231,22 +270,28 @@ func addFriendByName(userId: String, friendName: String, completion: @escaping ( } } + // Start the task task.resume() } +// Function to delete a friend by name func deleteFriendByName(userId: String, friendName: String, completion: @escaping (Result) -> Void) { + // API URL let urlString = "https://here-swe.vercel.app/user/\(userId)/friends_name" + // URL validation guard let url = URL(string: urlString) else { completion(.failure(URLError(.badURL))) return } - + + // Setup request var request = URLRequest(url: url) request.httpMethod = "DELETE" request.addValue("application/json", forHTTPHeaderField: "Content-Type") - request.addValue(apiKey2, forHTTPHeaderField: "X-API-Key") + request.addValue(apiKey, forHTTPHeaderField: "X-API-Key") + // Encode request body let requestBody = FriendRequest(friendName: friendName) do { request.httpBody = try JSONEncoder().encode(requestBody)