기능 구현 #6
트윗 작성
트윗 작성
| NewTweetView
struct NewTweetView: View {
@State private var caption = ""
@Environment(\.dismiss) var dismiss
var body: some View {
VStack {
HStack {
Button {
dismiss()
} label: {
Text("Cancel")
.foregroundColor(Color(.systemBlue))
}
Spacer()
Button {
print("tweet")
} label: {
Text("Tweet")
.bold()
.foregroundColor(.white)
.padding(.horizontal)
.padding(.vertical, 8)
.background(Color(.systemBlue))
.clipShape(Capsule())
}
}
.padding()
HStack(alignment: .top) {
Circle()
.frame(width: 64, height: 64)
TextArea("What's happening?", text: $caption)
}
.padding()
}
}
}
이전에 작성했던 NewTweetView는 아직도 임시 View를 프로필 사진 대신 표시하고 있다.
지금 트윗을 작성하려는 계정의 프로필 사진을 표시하도록 코드를 수정한다.
import SwiftUI
import Kingfisher
struct NewTweetView: View {
@State private var caption = ""
@Environment(\.dismiss) var dismiss
@EnvironmentObject var authViewModel: AuthViewModel
.
.
.
HStack(alignment: .top) {
if let user = authViewModel.currentUser {
KFImage(URL(string: user.profileImageUrl))
.resizable()
.scaledToFill()
.clipShape(Circle())
.frame(width: 64, height: 64)
}
.
.
.
로그인이 유효한지 확인하기 위해 FirebaseAuth를 사용해야 한다.
따라서 main에서 전달한 AuthViewModel을 사용하기 위해 EnvironmentObject 인스턴스를 선언한다.
Kingfisher를 import 하고, currentUser를 확인해 profileImageUrl을 KFImage 메서드로 전달한다.
트윗 작성
| TweetService
NewTweetView에서 작성한 글을 DB에 업로드할 메서드를 작성한다.
import Firebase
struct TweetService {
func uploadTweet(caption: String, completion: @escaping(Bool) -> Void) {
}
}
메서드의 이름은 uploadTweet이고, 내용을 caption 파라미터로 전달받아 결과를 CompletionHandler로 전달한다.
import Firebase
struct TweetService {
func uploadTweet(caption: String, completion: @escaping(Bool) -> Void) {
guard let uid = Auth.auth().currentUser?.uid else {
return
}
}
}
guard-let으로 FirebaseAuth의 우선 트윗을 올릴 계정의 유효성을 검사한다.
import Firebase
struct TweetService {
func uploadTweet(caption: String, completion: @escaping(Bool) -> Void) {
guard let uid = Auth.auth().currentUser?.uid else {
return
}
let data = ["uid": uid,
"caption": caption,
"likes": 0,
"timestamp": Timestamp(date: Date())] as [String : Any]
}
}
이제 Firebase에 업로드하기 위한 데이터를 dictionary로 구성한다.
uid는 현재 접속중인 사용자의 uid,
caption은 파라미터로 전달받은 트윗의 내용,
likes는 좋아요의 카운트,
timestamp는 트윗을 작성하는 시간에 해당한다.
import Firebase
struct TweetService {
func uploadTweet(caption: String, completion: @escaping(Bool) -> Void) {
guard let uid = Auth.auth().currentUser?.uid else {
return
}
let data = ["uid": uid,
"caption": caption,
"likes": 0,
"timestamp": Timestamp(date: Date())] as [String : Any]
Firestore.firestore().collection("tweets").document().setData(data) {
}
}
}
데이터의 저장을 위해서는 Firestore의 저장할 경로로 접근해 setData 메서드를 호출하면 된다.
import Firebase
struct TweetService {
func uploadTweet(caption: String, completion: @escaping(Bool) -> Void) {
guard let uid = Auth.auth().currentUser?.uid else {
return
}
let data = ["uid": uid,
"caption": caption,
"likes": 0,
"timestamp": Timestamp(date: Date())] as [String : Any]
Firestore.firestore().collection("tweets").document().setData(data) { error in
if let error = error {
print("Failed to upload \(error.localizedDescription)")
completion(false)
return
}
completion(true)
}
}
}
completionHandler로는 setData 메서드의 결과가 전달된다.
error로 바인딩해 값이 존재한다면 실패로 판단하고 completionHandler로 false를 전달하고 종료하며,
값이 존재하지 않는다면 성공으로 판단하고 completionHandler로 true를 전달한다.
트윗 작성
| UploadTweetViewModel
TweetService에서 정의한 메서드를 NewTweetView에서 사용할 수 있도록 UploadTweetViewModel에 기능을 구현한다.
class UploadTweetViewModel: ObservableObject {
@Published var didUploadTweet = false
let service = TweetService()
}
UploadTweetViewModel에서 Firebase를 사용할 수 있도록 TweetService 인스턴스를 생성하고,
TweetService에서 정의한 메서드 uploadTweet의 결과를 저장할 published 변수 didUploadTweet를 선언한다.
class UploadTweetViewModel: ObservableObject {
@Published var didUploadTweet = false
let service = TweetService()
func uploadTweet(withCaption caption: String) {
service.uploadTweet(caption: caption) { result in
if result {
self.didUploadTweet = true
} else {
//show error
}
}
}
}
NewTweetView에서 호출할 수 있도록 uploadTweet 메서드를 재정의 한다.
caption을 파라미터로 전달받아, TweetService의 uploadTweet 메서드에 전달한다.
이후 uploadTweet 메서드의 결과에 따라 CompletionHandler에서 분기해 uploadTweet의 상태를 변경한다.
트윗 작성
| NewTweetView에 연결하기
이제 기능적인 부분이 완성 됐으므로 Tweet을 작성하는 화면인 NewTweetView에 연결하기만 하면 된다.
import SwiftUI
import Kingfisher
struct NewTweetView: View {
@State private var caption = ""
@Environment(\.dismiss) var dismiss
@EnvironmentObject var authViewModel: AuthViewModel
@ObservedObject var viewModel = UploadTweetViewModel()
.
.
.
앞서 작성했던 UploadTweetViewModel 인스턴스를 생성한다.
.
.
.
var body: some View {
VStack {
HStack {
Button {
dismiss()
} label: {
Text("Cancel")
.foregroundColor(Color(.systemBlue))
}
Spacer()
Button {
viewModel.uploadTweet(withCaption: caption)
} label: {
Text("Tweet")
.bold()
.foregroundColor(.white)
.padding(.horizontal)
.padding(.vertical, 8)
.background(Color(.systemBlue))
.clipShape(Capsule())
}
}
.padding()
.
.
.
로그를 표시하던 버튼을 UploadTweetViewModel의 uploadTweet 메서드를 호출하도록 수정한다.
.
.
.
TextArea("What's happening?", text: $caption)
}
.padding()
}
.onReceive(viewModel.$didUploadTweet) { result in
if result {
dismiss()
}
}
}
}
트윗 업로드가 끝나게 되면, flag로 사용하려고 생성했던 didUploadTweet이 변경된다.
해당 값이 변경되면 반응할 수 있도록 onReceive modifier를 사용해 dismiss를 호출하면
업로드가 완료될 시 자동으로 NewTweetView가 사라지고 이전의 화면으로 돌아가도록 구현할 수 있다.
'프로젝트 > Twitter Clone App (w∕Firebase)' 카테고리의 다른 글
20. 기능 구현 #8 (0) | 2023.01.17 |
---|---|
19. 기능 구현 #7 (0) | 2023.01.14 |
17. 기능 구현 #5 (0) | 2023.01.12 |
16. 코드 가독성 개선 (0) | 2023.01.11 |
15. 기능구현 #4 (0) | 2023.01.11 |