Final (8)
리뷰 작성글을 보여지게 구현
1. 모델링
이젠 Firebase의 모델링이 중요해진다.
부리더인 미림님이 구현한 VC에서 유져가 작성한 글을 올리는 걸 해보려한다.
물론 모델링의 회의도 같이 해보았다.
Collection은 UserReview
로 하기로했다.
우선 모델링은 둘이서 회의를 하면서 진행했고 다음과 같다
1
2
3
4
5
6
7
8
9
10
11
12
13
14
struct ReviewModel {
var uid: String
var title: String
var storeAddress: String
var content: String
var rating: Float
var imageURL: [String]
var isActivate: Bool
var createdAt: Timestamp
var updatedAt: Timestamp
}
2. UserManager에 구현
1
2
3
func writeReview(userDict: [String: Any], completion: (((Error)?) -> Void)?) {
reviewCollection.addDocument(data: userDict, completion: completion)
}
이렇게 적었다.
3. ViewModel
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class ReviewViewModel {
private let userManager: UserManager
var reviewPublisher = PassthroughSubject<Void, Error>()
func createReview(userDict: [String: Any]) {
userManager.writeReview(userDict: userDict) { [weak self] error in
guard let self = self else { return }
if let error = error {
self.reviewPublisher.send(completion: .failure(error))
}
}
}
}
일단은 이렇게 넘기기로 결정
4. VC작성
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
@objc func submitButtonTapped() {
guard
let uid = Auth.auth().currentUser?.uid,
let title = titleTextField.text,
let content = contentTextView.text
else {
return
}
uploadImages(images: selectedImages)
.sink(receiveCompletion: { completion in
switch completion {
case .finished:
break
case .failure(let error):
let alert = UIAlertController(title: "에러 발생", message: "\(error.localizedDescription)이 발생했습니다.", preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "확인", style: .default))
self.present(alert, animated: true)
}
}, receiveValue: { [weak self] imageURLs in
guard let self = self else { return }
let dictionary: [String: Any] = [
"uid": uid,
"title": title,
"storeAddress": addressText,
"storeName": storeTitleText,
"content": content,
"rating": selectedRating,
"imageURL": imageURLs,
"isActivate": false,
"createdAt": Timestamp(date: Date())
]
self.viewModel.createReview(userDict: dictionary)
})
.store(in: &cancellables)
let alert = UIAlertController(title: "리뷰 저장", message: "리뷰가 등록 되었습니다.", preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "확인", style: .default, handler: { [unowned self] _ in
dismiss(animated: true)
}))
present(alert, animated: true)
}
이미지를 여러개 등록
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
func uploadImage(image: UIImage) -> AnyPublisher<String, Error> {
Future<String, Error> { promise in
let storageRef = Storage.storage().reference()
guard let imageData = image.jpegData(compressionQuality: 0.5) else {
return
}
let imageRef = storageRef.child("images/\(UUID().uuidString).jpg")
imageRef.putData(imageData, metadata: nil) { metadata, error in
if let error = error {
promise(.failure(error))
return
}
imageRef.downloadURL { url, error in
if let error = error {
promise(.failure(error))
} else if let downloadURL = url {
promise(.success(downloadURL.absoluteString))
}
}
}
}
.eraseToAnyPublisher()
}
func uploadImages(images: [UIImage]) -> AnyPublisher<[String], Error> {
let publishers = images.map { uploadImage(image: $0) }
return Publishers.MergeMany(publishers)
.collect()
.eraseToAnyPublisher()
}
- uploadImage
- Future를 사용하여 이미지를 Firebase Storage에 업로드하고, 업로드가 완료되면 다운로드 URL을 반환하는 AnyPublisher<String, Error>를 생성
- Future 초기화: Future는 비동기 작업의 결과를 promise를 통해 리턴
- 이미지 데이터 변환: 이미지를 JPEG 데이터로 변환합니다. 실패 시 promise를 반환하지 않고 종료
- Firebase Storage 참조 생성: Storage.storage().reference()를 사용하여 Firebase Storage 참조를 생성
- 이미지 업로드: imageRef.putData(imageData, metadata: nil)를 사용하여 이미지를 업로드. 업로드가 완료되면 다운로드 URL을 요청
- 결과 반환: 다운로드 URL을 성공적으로 가져오면 promise(.success(downloadURL.absoluteString))을 호출하여 URL을 반환. 에러 발생 시 promise(.failure(error))를 호출
- uploadImages
- 여러 uploadImage 호출을 Combine의 Publishers.MergeMany와 collect를 사용하여 결합한 AnyPublisher<[String], Error>를 반환
- map 사용: 각 이미지를 uploadImage 함수로 매핑하여 AnyPublisher<String, Error>의 배열을 생성
- Publishers.MergeMany: 이 연산자는 배열의 모든 퍼블리셔를 결합하여 하나의 퍼블리셔로 만듦
- 각 퍼블리셔의 출력은 하나의 스트림으로 합쳐지게 된다. - collect: 모든 퍼블리셔가 완료될 때까지 대기한 후, 각 퍼블리셔의 출력값을 배열로 저장
이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.