diff --git a/HappyAnding/HappyAnding/Model/Answer.swift b/HappyAnding/HappyAnding/Model/Answer.swift index 188ab76f..077f3cea 100644 --- a/HappyAnding/HappyAnding/Model/Answer.swift +++ b/HappyAnding/HappyAnding/Model/Answer.swift @@ -21,7 +21,7 @@ struct Answer: Identifiable, Codable, Equatable, Hashable { var likedBy: [String:Bool] var likeCount: Int - init(content: String, author: String, postId:String, images: [String] = [], thumbnailImages: [String] = []) { + init(content: String, author: String, postId:String) { self.id = UUID().uuidString self.createdAt = Date().getDate() @@ -30,8 +30,8 @@ struct Answer: Identifiable, Codable, Equatable, Hashable { self.isAccepted = false self.author = author self.postId = postId - self.images = images - self.thumbnailImages = thumbnailImages + self.images = [] + self.thumbnailImages = [] self.likeCount = 0 self.likedBy = [:] diff --git a/HappyAnding/HappyAnding/Model/Post.swift b/HappyAnding/HappyAnding/Model/Post.swift index 7957dc6b..2406b098 100644 --- a/HappyAnding/HappyAnding/Model/Post.swift +++ b/HappyAnding/HappyAnding/Model/Post.swift @@ -22,7 +22,7 @@ struct Post: Identifiable, Codable, Equatable, Hashable { var likeCount: Int var commentCount: Int - init(type: PostType, content: String, author: String, shortcuts: [String] = [], images: [String] = [], thumbnailImages: [String] = []) { + init(type: PostType, content: String, author: String, shortcuts: [String] = []) { self.id = UUID().uuidString self.createdAt = Date().getDate() @@ -31,8 +31,8 @@ struct Post: Identifiable, Codable, Equatable, Hashable { self.content = content self.author = author self.shortcuts = shortcuts - self.images = images - self.thumbnailImages = thumbnailImages + self.images = [] + self.thumbnailImages = [] self.likeCount = 0 self.commentCount = 0 diff --git a/HappyAnding/HappyAnding/Repository/CommunityRepository.swift b/HappyAnding/HappyAnding/Repository/CommunityRepository.swift index 8e8b05ee..bbeb176d 100644 --- a/HappyAnding/HappyAnding/Repository/CommunityRepository.swift +++ b/HappyAnding/HappyAnding/Repository/CommunityRepository.swift @@ -33,8 +33,11 @@ class CommunityRepository { let imageData = image.pngData()! let imageId = UUID().uuidString let imageRef = storage.child("community/\(imageId).png") + + let metadata = StorageMetadata() + metadata.contentType = "image/png" - imageRef.putData(imageData, metadata: nil) { metadata, error in + imageRef.putData(imageData, metadata: metadata) { metadata, error in if let error = error { print(error.localizedDescription) imageUploadGroup.leave() @@ -68,32 +71,30 @@ class CommunityRepository { private func deleteImages(with urls: [String], completion: @escaping (Bool) -> Void) { let storage = Storage.storage() - - // 카운터를 사용하여 모든 삭제 작업이 완료되었는지 추적합니다. - var deleteCount = 0 + let dispatchGroup = DispatchGroup() + var deleteErrors = false for url in urls { - // URL로부터 참조를 얻습니다. let ref = storage.reference(forURL: url) - // 참조를 사용하여 이미지 삭제 + dispatchGroup.enter() + ref.delete { error in if let error = error { deleteErrors = true } - - deleteCount += 1 - if deleteCount == urls.count { - completion(!deleteErrors) - } + dispatchGroup.leave() } } + + dispatchGroup.notify(queue: .main) { + completion(!deleteErrors) + } } - @@ -140,31 +141,15 @@ class CommunityRepository { } } - - // 글 생성 -// func createPost(post: Post, completion: @escaping (Bool) -> Void) { -// let documentId = post.id -// do { -// try db.collection(postCollection).document(documentId).setData(from: post) { error in -// if error != nil { -// completion(false) -// } else { -// completion(true) -// } -// } -// } catch { -// completion(false) -// } -// } -// - - // 글 생성 with images + + // 글 생성 func createPost(post: Post, images: [UIImage]? = nil, thumbnailImages: [UIImage]? = nil, completion: @escaping (Bool) -> Void) { let documentId = post.id do { try db.collection(postCollection).document(documentId).setData(from: post) { error in if let error = error { + print(error.localizedDescription) completion(false) return } @@ -172,6 +157,7 @@ class CommunityRepository { var imageURLs: [String] = [] var thumbnailURLs: [String] = [] let dispatchGroup = DispatchGroup() + // 일반 이미지 업로드 if let images = images, !images.isEmpty { @@ -208,13 +194,13 @@ class CommunityRepository { if !updateData.isEmpty { self.db.collection(self.postCollection).document(documentId).updateData(updateData) { error in if let error = error { + print(error.localizedDescription) completion(false) } else { completion(true) } } } else { - // 업데이트할 데이터가 없으면 성공으로 처리 completion(true) } } @@ -224,35 +210,10 @@ class CommunityRepository { } } - -// // 글 업데이트 -// func updatePost(postid: String, content: String? = nil, shortcuts: [String]? = nil, images: [UIImage]? = nil, thumbnailImages: [UIImage]? = nil, completion: @escaping (Bool) -> Void) { -// -// var updateFields: [String: Any] = [:] -// -// if let content = content { -// updateFields["content"] = content -// } -// if let shortcuts = shortcuts { -// updateFields["shortcuts"] = shortcuts -// } -// -// if !updateFields.isEmpty { -// db.collection(postCollection).document(postid).updateData(updateFields) { error in -// if error != nil { -// completion(false) -// } else { -// completion(true) -// } -// } -// } else { -// completion(true) -// } -// } - + // 글 업데이트 func updatePost(postId: String, content: String? = nil, shortcuts: [String]? = nil, images: [UIImage]? = nil, thumbnailImages: [UIImage]? = nil, completion: @escaping (Bool) -> Void) { - let db = Firestore.firestore() - let documentRef = db.collection("posts").document(postId) + + let documentRef = db.collection(postCollection).document(postId) var updateFields: [String: Any] = [:] if let content = content { @@ -264,11 +225,12 @@ class CommunityRepository { documentRef.updateData(updateFields) { error in if let error = error { + print(error.localizedDescription) completion(false) return } + - // 이미지 업데이트 로직 guard images != nil || thumbnailImages != nil else { completion(true) return @@ -322,36 +284,37 @@ class CommunityRepository { let group = DispatchGroup() var overallSuccess = true - // 게시물의 이미지 URL을 가져오고 삭제 + let documentRef = db.collection(postCollection).document(postId) + group.enter() - db.collection(postCollection).document(postId).getDocument { document, error in + documentRef.getDocument { document, error in if let document = document, document.exists { let imageUrls = document.data()?["images"] as? [String] ?? [] let thumbnailUrls = document.data()?["thumbnailImages"] as? [String] ?? [] let allUrls = imageUrls + thumbnailUrls - - // 이미지 삭제 로직 - for url in allUrls { - let ref = Storage.storage().reference(forURL: url) - ref.delete { error in - if error != nil { + if !allUrls.isEmpty { + self.deleteImages(with: allUrls) { success in + if !success { overallSuccess = false } + group.leave() } + } else { + group.leave() } - } - group.leave() - } - - // 게시물 삭제 - group.enter() - db.collection(postCollection).document(postId).delete { error in - if error != nil { + + } else { overallSuccess = false } - group.leave() + + documentRef.delete { error in + if error != nil { + overallSuccess = false + } + } } + // 관련된 답변(Answer) 삭제 group.enter() db.collection(answerCollection).whereField("postId", isEqualTo: postId).getDocuments { (snapshot, error) in @@ -361,7 +324,11 @@ class CommunityRepository { return } for document in documents { - document.reference.delete() + self.deleteAnswer(answerId:document.data()["id"] as? String ?? "") { success in + if (!success) { + overallSuccess = false + } + } } group.leave() } @@ -380,7 +347,6 @@ class CommunityRepository { group.leave() } - // 모든 삭제 작업이 완료되었는지 확인 group.notify(queue: .main) { completion(overallSuccess) } @@ -470,21 +436,7 @@ class CommunityRepository { } } -// func createAnswer(answer: Answer, completion: @escaping (Bool) -> Void) { -// let documentId = answer.id -// do { -// try db.collection(answerCollection).document(documentId).setData(from: answer) { error in -// if error != nil { -// completion(false) -// } else { -// completion(true) -// } -// } -// } catch { -// completion(false) -// } -// } - + // 답변 생성 func createAnswer(answer: Answer, images: [UIImage]? = nil, thumbnailImages: [UIImage]? = nil, completion: @escaping (Bool) -> Void) { let documentId = answer.id // Answer 객체의 ID를 사용 @@ -536,7 +488,6 @@ class CommunityRepository { completion(error == nil) } } else { - // 업데이트할 데이터가 없으면 성공으로 처리 completion(true) } } @@ -546,36 +497,10 @@ class CommunityRepository { } } -// func updateAnswer(answerId: String, content: String? = nil, images: [String]? = nil, completion: @escaping (Bool) -> Void) { -// -// var updateFields: [String: Any] = [:] -// -// if let newContent = content { -// updateFields["content"] = newContent -// } -// -// if let newImages = images { -// updateFields["images"] = newImages -// } -// -// if !updateFields.isEmpty { -// db.collection(answerCollection).document(answerId).updateData(updateFields) { error in -// if error != nil { -// completion(false) -// } else { -// completion(true) -// } -// } -// } else { -// completion(true) -// } -// } - - - - + + // 답변 업데이트 func updateAnswer(answerId: String, content: String? = nil, images: [UIImage]? = nil, thumbnailImages: [UIImage]? = nil, completion: @escaping (Bool) -> Void) { - let db = Firestore.firestore() + let documentRef = db.collection(answerCollection).document(answerId) var updateFields: [String: Any] = [:] @@ -635,6 +560,7 @@ class CommunityRepository { } } + // 답변 채택 func acceptAnswer(answerId: String, completion: @escaping (Bool) -> Void) { let answerRef = db.collection(answerCollection).document(answerId) @@ -647,26 +573,12 @@ class CommunityRepository { } } - -// func deleteAnswer(answerId: String, completion: @escaping (Bool) -> Void) { -// db.collection(answerCollection).document(answerId) -// .delete() { error in -// if error != nil { -// completion(false) -// } else { -// completion(true) -// } -// } -// } - - - + // 답변 삭제 func deleteAnswer(answerId: String, completion: @escaping (Bool) -> Void) { let db = Firestore.firestore() let group = DispatchGroup() var overallSuccess = true - // 답변의 이미지 URL을 가져오고 삭제 group.enter() db.collection(answerCollection).document(answerId).getDocument { document, error in if let document = document, document.exists { @@ -674,34 +586,34 @@ class CommunityRepository { let thumbnailUrls = document.data()?["thumbnailImages"] as? [String] ?? [] let allUrls = imageUrls + thumbnailUrls - // 이미지 삭제 로직 - for url in allUrls { - let ref = Storage.storage().reference(forURL: url) - ref.delete { error in - if error != nil { + if !allUrls.isEmpty { + self.deleteImages(with: allUrls) { success in + if !success { overallSuccess = false } + group.leave() } + } else { + group.leave() } - } - group.leave() - } - - // 답변 삭제 - group.enter() - db.collection(answerCollection).document(answerId).delete { error in - if error != nil { + } else { overallSuccess = false + group.leave() + } + + db.collection(self.answerCollection).document(answerId).delete { error in + if error != nil { + overallSuccess = false + } } - group.leave() } - // 모든 삭제 작업이 완료되었는지 확인 group.notify(queue: .main) { completion(overallSuccess) } } + // 답변 좋아요 func likeAnswer(answerId: String, userId: String, completion: @escaping (Bool) -> Void) { let answerRef = db.collection(answerCollection).document(answerId) @@ -733,6 +645,8 @@ class CommunityRepository { } } + + // 답변 좋아요 취소 func unlikeAnswer(answerId: String, userId: String, completion: @escaping (Bool) -> Void) { let answerRef = db.collection(answerCollection).document(answerId) @@ -822,8 +736,6 @@ class CommunityRepository { if !updates.isEmpty { db.collection(communityCommentCollection).document(commentId).updateData(updates) { error in if let error = error { - print("안녕") - print(error) completion(false) } else { completion(true) diff --git a/HappyAnding/HappyAndingTests/CommunityRepositoryTest.swift b/HappyAnding/HappyAndingTests/CommunityRepositoryTest.swift index f38c5fd4..26789236 100644 --- a/HappyAnding/HappyAndingTests/CommunityRepositoryTest.swift +++ b/HappyAnding/HappyAndingTests/CommunityRepositoryTest.swift @@ -23,18 +23,20 @@ class FirestoreTests: XCTestCase { super.tearDown() } - private let testPostId = "4B7F5702-EF0C-4C51-95F9-4CDCA499A58B" - private let testAnswerId = "E0B83A68-9536-4976-BD64-41DCB21DB304" + private let testPostId = "8F242D05-3FB4-4604-880A-93A99B3F77AF" + private let testAnswerId = "CECD1EBA-2157-41ED-AA24-AEBD4D105272" private let testCommentId = "C644F4BF-5F32-4F67-B2D8-18F182A1FB2C" //MARK: - 글 관련 테스트 + + // 모든 글 가져오기 테스트 - func testGetPosts() { + func testGetAllPosts() { let expectation = self.expectation(description: "getPosts") - repository.getPosts { posts in + repository.getAllPosts { posts in print("\n") @@ -50,11 +52,11 @@ class FirestoreTests: XCTestCase { waitForExpectations(timeout: 5, handler: nil) } - + // 무한스크롤 글 가져오기 함수 테스트 - func testGetPosts2() { + func testGetPosts() { let expectation = self.expectation(description: "getPosts") - let limit = 10 // + let limit = 10 let lastCreatedAt: String? = "20240411102228" repository.getPosts(limit: limit, lastCreatedAt: lastCreatedAt) { posts in @@ -91,12 +93,32 @@ class FirestoreTests: XCTestCase { waitForExpectations(timeout: 5, handler: nil) } - // 글 업데이트 테스트 + // 글 생성 테스트 with Images + func testCreatePostWithImages() { + let expectation = self.expectation(description: "Completion handler invoked") + + let testPost = Post(type:PostType.General, content: "This is a test post", author:"1") + let testImages = [UIImage(named: "updateAppIcon")!, UIImage(named: "updateAppIcon")!] + let testthumbnailImages = [UIImage(named: "updateAppIcon")!, UIImage(named: "updateAppIcon")!] + + repository.createPost(post: testPost, images: testImages, thumbnailImages: testthumbnailImages) { success in + + XCTAssertTrue(success, "Post Create should succed.") + expectation.fulfill() + } + + waitForExpectations(timeout: 5, handler: nil) + } + + // 글 업데이트 테스트 func testUpdatePost() { let expectation = self.expectation(description: "updatePost") - let postid = testPostId - repository.updatePost(postid: postid, content: "Updated Content", shortcuts: ["Shortcut1"], images: ["ImageURL1"]) { success in + let testImages = [UIImage(named: "updateAppIcon")!, UIImage(named: "updateAppIcon")!] + let testthumbnailImages = [UIImage(named: "updateAppIcon")!, UIImage(named: "updateAppIcon")!] + + let postId = testPostId + repository.updatePost(postId: postId, content: "Updated Content", shortcuts: ["Shortcut1"], images:testImages, thumbnailImages: testthumbnailImages) { success in XCTAssertTrue(success, "Post update should succeed.") expectation.fulfill() } @@ -114,7 +136,7 @@ class FirestoreTests: XCTestCase { expectation.fulfill() } - waitForExpectations(timeout: 5, handler: nil) + waitForExpectations(timeout: 20, handler: nil) } @@ -185,13 +207,34 @@ class FirestoreTests: XCTestCase { waitForExpectations(timeout: 5, handler: nil) } + + // 새로운 답변을 생성하는 함수 with Images 테스트 + func testCreateAnswerWithImages() { + let expectation = self.expectation(description: "createAnswer") + let answer = Answer(content: "Test answer", author: "1", postId: testPostId) + + let testImages = [UIImage(named: "updateAppIcon")!, UIImage(named: "updateAppIcon")!] + let testthumbnailImages = [UIImage(named: "updateAppIcon")!, UIImage(named: "updateAppIcon")!] + + repository.createAnswer(answer: answer, images: testImages, thumbnailImages: testthumbnailImages) { success in + XCTAssertTrue(success, "Answer creation should succeed.") + expectation.fulfill() + } + + waitForExpectations(timeout: 5, handler: nil) + } + + // 기존의 답변을 업데이트하는 함수 테스트 func testUpdateAnswer() { let expectation = self.expectation(description: "updateAnswer") let answerId = testAnswerId - repository.updateAnswer(answerId: answerId, content: "Updated content", images: ["UpdatedImageURL"]) { success in + let testImages = [UIImage(named: "updateAppIcon")!, UIImage(named: "updateAppIcon")!] + let testthumbnailImages = [UIImage(named: "updateAppIcon")!, UIImage(named: "updateAppIcon")!] + + repository.updateAnswer(answerId: answerId, content: "Updated content", images: testImages, thumbnailImages: testthumbnailImages) { success in XCTAssertTrue(success, "Answer update should succeed.") expectation.fulfill() }