TCA를 통한 Firebase 연동
firebase with TCA
    firebase,tca
    서론
- 현재 진행 중인 프로젝트 Tinowledge를 TCA로 구현하려고 하는데, Firebase 에 대한 참고자료가 존재하지 않는 상황
- 필요한 기능
    - FirebaseAPI 연동
        - FirebaseAPIClient 생성 및 Environment에 선언
- 데이터 조회
- 데이터 입력
 
 
- FirebaseAPI 연동
        
본론
FirebaseAPIClient 생성
- 최초 APIClient에는 필요한 action을 추상화하여 선언한다.
    - fetchTins : Tinowledge배열을 가져온다.
- addTin : Tinowledge를 입력한다. ```swift public struct FirebaseAPIClient { public var fetchTins: () -> EffectTask<[Tinowledge]> public var addTin: (_ tin: Tinowledge) -> Effect<None, APIError>
 public init( fetchTins: @escaping () -> EffectTask<[Tinowledge]>, addTin: @escaping (_ tin: Tinowledge) -> Effect<None, APIError>) { self.fetchTins = fetchTins self.addTin = addTin } } ``` 
- fetchTins : 
- 실제 Firebase에 접근하기 위해 extension으로 구체적인 변수들을 선언한다.
    public extension FirebaseAPIClient { static let db: Firestore = Firestore.firestore() static var tins: [Tinowledge] = [] static let live = Self( fetchTins: { }, addTin: { } ) }
데이터 조회 Action
fetchTins: {
	db.collection("Tinowledges")
		.addSnapshotListener { snap, error in
				if let error = error {
					print("fetch error: \(error)")
					return
				}
				tins = snap?.documents.compactMap { doc in
					try? doc.data(as: Tinowledge.self)
				} ?? []
			tins.shuffle()
			tins = Array(tins.prefix(5))
		}
	return .init(value: tins)
	}
데이터 입력 Action
addTin: { tin in
	let _ = try! db.collection("Tinowledges")
		.document(tin.id)
		.setData(from: tin)
	return .none
}
최종 코드
import Combine
import ComposableArchitecture
import Foundation
import SwiftUI
import Firebase
import FirebaseFirestore
import FirebaseFirestoreSwift
public struct FirebaseAPIClient {
	public var fetchTins: () -> EffectTask<[Tinowledge]>
	public var addTin: (_ tin: Tinowledge) -> Effect<None, APIError>
	public init(
		fetchTins: @escaping () -> EffectTask<[Tinowledge]>,
		addTin: @escaping (_ tin: Tinowledge) -> Effect<None, APIError>) {
			self.fetchTins = fetchTins
			self.addTin = addTin
		}
}
public extension FirebaseAPIClient {
	static let db: Firestore = Firestore.firestore()
	static var tins: [Tinowledge] = []
	static let live = Self(
		fetchTins: {
            print("api fetching...")
            DispatchQueue.global(qos: .background).sync {
            db.collection("Tinowledges")
                .addSnapshotListener { snap, error in
                        if let error = error {
                            print("fetch error: \(error)")
                            return
                        }
                        tins = snap?.documents.compactMap { doc in
                            try? doc.data(as: Tinowledge.self)
                        } ?? []
                    tins.shuffle()
                    tins = Array(tins.prefix(5))
                }
                print("DispatchQueue1")
            }
            print("DispatchQueue2")
			return .init(value: tins)
		},
		addTin: { tin in
			let _ = try! db.collection("Tinowledges")
				.document(tin.id)
				.setData(from: tin)
			return .none
		}
	)
}
결론
- 동작은 하지만, 원하는 대로 로직이 진행되지 않는 상황
    - 최초 View가 onAppear됐을 때, 바로 데이터 조회 action이 실행되어 화면을 그리고 싶은데, 빈 배열 형태로 success가 return 되는 현상이 있다.
 
- 최초 View가 onAppear됐을 때, 바로 데이터 조회 action이 실행되어 화면을 그리고 싶은데, 빈 배열 형태로