UIKIT

Firestore에 새로운 트윗 저장 + Combine 비동기 처리

밤새는 탐험가89 2025. 1. 9. 14:51

https://explorer89.tistory.com/304

 

Firestore 데이터베이스에서 문서 업데이트, Combine 비동기작업

https://explorer89.tistory.com/303 Firebase - Combine 프레임워크 사용 비동기 작업 처리 (저장) map VS flatmaphttps://explorer89.tistory.com/302 collectionUsers(retreive id: String)https://explorer89.tistory.com/301 CreateUser 함수?https

explorer89.tistory.com

 

func collectionTweets(dispatch tweet: Tweet) -> AnyPublisher<Bool, Error> {
    db.collection(tweetsPath).document(tweet.id).setData(from: tweet)
        .map { _ in true}
        .eraseToAnyPublisher()
}

 

주어진 함수는 Firestore 데이터베이스의 특정 컬렉션(tweetsPath)에 Tweet 객체를 저장하고, Combine의 AnyPublisher로 작업 결과를 반환하는 함수입니다.

 

 

  • func collectionTweets(dispatch tweet: Tweet):
    • dispatch라는 이름으로 Tweet 객체를 파라미터로 받습니다.
    • 이 객체를 Firestore의 tweetsPath 컬렉션에 저장합니다.
  • -> AnyPublisher<Bool, Error>:
    • Firestore 데이터 저장 작업의 결과를 Combine의 AnyPublisher로 반환합니다.
    • 성공 시 Bool 타입의 true 값을 방출하고, 실패 시 Error를 방출합니다.

 

db.collection(tweetsPath).document(tweet.id).setData(from: tweet)

 

  • db.collection(tweetsPath):
    • Firestore의 tweetsPath라는 이름의 컬렉션을 참조합니다.
    • tweetsPath는 Firestore에서 트윗 데이터를 저장하는 경로입니다.
  • .document(tweet.id):
    • tweet.id를 키로 하는 문서를 지정합니다.
    • 동일한 ID의 문서가 이미 존재하면 덮어쓰기가 발생합니다.
  • .setData(from: tweet):
    • Firestore의 setData 메서드를 사용해 Tweet 객체의 데이터를 문서에 저장합니다.
    • 이 메서드는 Encodable을 준수하는 객체(Tweet)를 Firestore에 저장할 수 있도록 지원합니다.
    • Firestore에서 비동기 작업으로 실행되며, 성공 또는 실패 여부를 클로저로 전달합니다. 
.map { _ in true }
.eraseToAnyPublisher()

 

  • .map { _ in true }:
    • Firestore 작업이 성공하면 반환 값을 무시하고 항상 true를 방출하도록 설정합니다.
    • true는 작업이 성공적으로 완료되었음을 나타냅니다.
  • .eraseToAnyPublisher():
    • 반환 타입을 AnyPublisher<Bool, Error>로 제한합니다.
    • 이는 호출자가 Combine 구현 세부사항에 종속되지 않도록 추상화하는 데 사용됩니다.

 

수정된 코드 

func collectionTweets(dispatch tweet: Tweet) -> AnyPublisher<Bool, Error> {
    Future<Bool, Error> { promise in
        db.collection(tweetsPath).document(tweet.id).setData(from: tweet) { error in
            if let error = error {
                promise(.failure(error)) // Firestore 작업 실패
            } else {
                promise(.success(true)) // Firestore 작업 성공
            }
        }
    }
    .eraseToAnyPublisher()
}

 

 

수정 사항

  1. Future 사용:
    • Firestore의 비동기 setData 작업을 Combine 스트림으로 감싸기 위해 Future를 사용했습니다.
    • promise를 통해 작업 성공 시 true, 실패 시 Error를 반환합니다.
  2. 에러 처리 추가:
    • Firestore 작업 실패 시 에러를 promise(.failure(error))로 처리하여 Combine 스트림에 전달합니다.
  3. eraseToAnyPublisher 유지:
    • 반환 타입을 AnyPublisher<Bool, Error>로 제한하여 추상화를 유지합니다.

 

func dispatchTweet() {
    guard let user = user else { return }
    let tweet = Tweet(author: user, tweetContent: tweetContent, likesCount: 0, likers: [], isReply: false, parentReference: nil)
    DatabaseManager.shared.collectionTweets(dispatch: tweet)
        .sink { [weak self] completion in
            if case .failure(let error) = completion {
                self?.error = error.localizedDescription
            }
        } receiveValue: { [weak self] state in
            self?.shouldDismissComposer = state
        }
        .store(in: &subscriptions)

}

 

주어진 함수는 dispatchTweet이라는 이름으로, Firestore에 새로운 트윗(Tweet 객체)을 저장하고, 성공/실패 여부를 Combine을 통해 처리합니다. 이전에 설명한 collectionTweets 함수를 사용하고 있으며, 트윗 저장과 관련된 비동기 작업을 수행하는 구조입니다.

 

func dispatchTweet() {
    guard let user = user else { return }
    ...
}

 

  • dispatchTweet:
    • 현재 사용자가 작성한 트윗을 데이터베이스에 저장하는 함수입니다.
    • 내부에서 트윗 데이터(Tweet 객체)를 생성하고, Firestore에 저장 요청을 보냅니다.
  • guard let user = user:
    • user가 nil이 아닌지 확인합니다. 사용자가 nil이라면 작업을 중단합니다.
    • user는 현재 트윗을 작성하는 사용자를 나타냅니다.
let tweet = Tweet(author: user, tweetContent: tweetContent, likesCount: 0, likers: [], isReply: false, parentReference: nil)

 

 

  • Tweet 객체 생성:
    • 트윗 데이터를 표현하는 모델입니다.
    • Tweet의 필드:
      • author: 트윗 작성자 (현재 로그인된 사용자).
      • tweetContent: 트윗 내용 (작성된 텍스트).
      • likesCount: 좋아요 수 (초기값: 0).
      • likers: 좋아요를 누른 사용자 목록 (초기값: 빈 배열).
      • isReply: 답글 여부 (초기값: false).
      • parentReference: 답글일 경우 부모 트윗의 참조 (초기값: nil).
DatabaseManager.shared.collectionTweets(dispatch: tweet)
    .sink { [weak self] completion in
        if case .failure(let error) = completion {
            self?.error = error.localizedDescription
        }
    } receiveValue: { [weak self] state in
        self?.shouldDismissComposer = state
    }
    .store(in: &subscriptions)

 

 

  • DatabaseManager.shared.collectionTweets(dispatch: tweet):
    • collectionTweets 함수 호출.
    • Firestore의 tweetsPath 컬렉션에 Tweet 객체를 저장합니다.
    • collectionTweets는 Combine의 AnyPublisher<Bool, Error>를 반환합니다.
      • 성공 시: true를 방출.
      • 실패 시: Firestore 오류를 방출.
  • sink:
    • Combine의 sink를 사용하여 비동기 작업 결과를 처리합니다.
    • completion:
      • 작업 완료 시 호출됩니다.
      • 실패 (.failure):
        • error.localizedDescription을 통해 에러 메시지를 저장하고 UI에 반영합니다.
      • 성공 (.finished):
        • 별도의 작업을 수행하지 않습니다.
    • receiveValue:
      • Firestore 저장 성공 시 호출됩니다.
      • state 값을 shouldDismissComposer에 저장하여 트윗 작성 창을 닫습니다.
  • store(in: &subscriptions):
    • Combine 구독을 subscriptions에 저장하여 메모리 누수를 방지합니다.
    • subscriptions는 Set<AnyCancellable> 타입이어야 합니다.