본문 바로가기

프로젝트/Twitter Clone App (w∕Firebase)

20. 기능 구현 #8

기능 구현 #8
TweetRowView 데이터 연결


TweetRowView 데이터 연결
| FeedViewModel

이전에 구현한 대로라면 FeedView가 표시되고, FeedViewModel을 호출하는 순간, fetchTweets 메서드가 작동하게 된다.

class FeedViewModel: ObservableObject {
    @Published var tweets = [Tweet]()
    let service = TweetService()

    init() {
        fetchTweets()
    }

    func fetchTweets() {
        service.fetchTweets { tweets in
            self.tweets = tweets
        }
    }
}

Tweet은 위와 같이 내용, 좋아요 카운트, 작성 날짜, 유저 정보(uid)를 가지고 있다.
필요한 정보를 전부 가지고 있는 것 같지만, 해당 유저의 계정, 이름, 프로필 사진 등 필요한 정보들은 존재하지 않는 상태다.

import Firebase
import FirebaseFirestoreSwift

struct UserService {
    func fetchUser(withUid uid: String, completion: @escaping(User) -> Void) {
        Firestore.firestore().collection("users").document(uid).getDocument { snapshot, _ in
            guard let snapshot = snapshot else {
                return
            }

            guard let user = try? snapshot.data(as: User.self) else {
                return
            }

            completion(user)
        }
    }

    func fetchUsers(completion: @escaping([User]) -> Void) {
        Firestore.firestore().collection("users").getDocuments { snapshot, _ in
            guard let documents = snapshot?.documents else {
                return
            }

            let users = documents.compactMap({ try? $0.data(as: User.self) })

            completion(users)
        }
    }
}

따라서 uid를 사용해 사용자의 정보를 가져 올 필요가 있고, 관련된 메서드는 이미 UserService에서 fetchUser로 구현한 바 있다.

class FeedViewModel: ObservableObject {
    @Published var tweets = [Tweet]()
    let service = TweetService()
    let userService = UserService()

    init() {
        fetchTweets()
    }

fetchUser 메서드를 사용할 수 있도록 FeedViewModel에 UserService 인스턴스를 생성한다.

Tweet를 받아오는 반환 값은 배열의 형태이며, 각각의 데이터는 Tweet 구조체로 이뤄져 있다.

struct Tweet: Identifiable, Codable {
    @DocumentID var id: String?
    let caption: String
    let timestamp: Timestamp
    let uid: String
    var likes: Int

    var user: User?
}

앞서 정의했던 Tweet 구조체는 DB에서 받아온 데이터를 SwiftUI에서 사용할 수 있도록 바인딩하는 역할을 한다.
그 와중에 아직 한 번도 사용하지 않았고, DB에 저장돼 있은 Tweet의 데이터도 아닌 변수가 하나 존재하는데,
바로 user다.

func fetchTweets() {
    service.fetchTweets { tweets in
        self.tweets = tweets

        for i in 0 ..< tweets.count {
            let uid = tweets[i].uid

            self.userService.fetchUser(withUid: uid) { user in
                self.tweets[i].user = user
            }
        }
    }
}

DB에서 받아 온 Tweet 배열을 하나씩 열거하며
작성자의 uid를 fetchUser 메서드에 전달해 작성자의 실제 정보를 user 변수에 저장한다.

TweetRowView 데이터 연결
| FeedRowView

before

var body: some View {
    VStack(alignment: .leading) {
        //profile image & user info & tweet
        HStack(alignment: .top, spacing: 12) {
            Circle()
                .frame(width: 56, height: 56)
                .foregroundColor(Color(.systemBlue))

            //user info & tweet caption
            VStack(alignment: .leading, spacing: 4) {
                //user info
                HStack {
                    Text("Marcus")
                        .font(.subheadline).bold()

                    Group {
                        Text("@philosophy")

                        Text("2d")
                    }
                    .foregroundColor(.gray)
                    .font(.caption)
                }

                //tweet caption
                Text(tweet.caption)
                    .font(.subheadline)
                    .multilineTextAlignment(.leading)
            }
        }
        .
        .
        .

after

var body: some View {
    VStack(alignment: .leading) {
        //profile image & user info & tweet
        if let user = tweet.user {
            HStack(alignment: .top, spacing: 12) {
                KFImage(URL(string: user.profileImageUrl))
                    .resizable()
                    .scaledToFill()
                    .clipShape(Circle())
                    .frame(width: 56, height: 56)

                //user info & tweet caption
                VStack(alignment: .leading, spacing: 4) {
                    //user info
                    HStack {
                        Text(user.fullname)
                            .font(.subheadline).bold()

                        Group {
                            Text("@\(user.username)")

                            Text("2d")
                        }
                        .foregroundColor(.gray)
                        .font(.caption)
                    }

                    //tweet caption
                    Text(tweet.caption)
                        .font(.subheadline)
                        .multilineTextAlignment(.leading)
                }
            }
        }
        .
        .
        .

모든 준비는 끝났다.
실제 데이터를 각각에 전달해 올바르게 표시하기만 하면 된다.

결과

 

'프로젝트 > Twitter Clone App (w∕Firebase)' 카테고리의 다른 글

22. 버그수정 #2  (0) 2023.01.18
21. DB와 연결하기 #3  (0) 2023.01.18
19. 기능 구현 #7  (0) 2023.01.14
18. 기능 구현 #6  (0) 2023.01.13
17. 기능 구현 #5  (0) 2023.01.12